Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/miloyip/rapidjson.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Hanson <smh@uk.ibm.com>2021-06-08 12:53:10 +0300
committerSteve Hanson <smh@uk.ibm.com>2021-06-08 12:53:10 +0300
commit28bcbd3f3578aa3890795dec5a97570d59d9256a (patch)
tree3ed270fc7e5784dd7e76a0e934eefa8629c49fae
parent494447b731b12160ac60a76a738d106863680129 (diff)
make std::string optional
-rw-r--r--include/rapidjson/internal/strfunc.h14
-rw-r--r--include/rapidjson/pointer.h5
-rw-r--r--include/rapidjson/schema.h15
-rw-r--r--include/rapidjson/uri.h522
-rw-r--r--test/unittest/pointertest.cpp43
-rw-r--r--test/unittest/uritest.cpp832
6 files changed, 1015 insertions, 416 deletions
diff --git a/include/rapidjson/internal/strfunc.h b/include/rapidjson/internal/strfunc.h
index baecb6cc..b698a8f4 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<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) {
diff --git a/include/rapidjson/pointer.h b/include/rapidjson/pointer.h
index b5e952b3..96f33fa8 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<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;
}
diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h
index 791aca54..a48288b9 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<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)) {
diff --git a/include/rapidjson/uri.h b/include/rapidjson/uri.h
index 98b0a158..640d793e 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 <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_() {}
+ //! Constructors
+ GenericUri(Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
+ //std::cout << "Default constructor" << std::endl;
+ }
- GenericUri(const String& uri) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_() {
- Parse(uri);
+ 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, SizeType len) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_() {
- Parse(String(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
+
+ //! 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();
+ 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;
+ }
+ return *this;
+ }
+
+ //! Getters
// Use with specializations of GenericValue
template<typename T> void Get(T& uri, Allocator& allocator) {
- uri.template Set<String>(this->Get(), allocator);
+ uri.template Set<const Ch*>(this->GetString(), allocator); // TypeHelper from document.h
}
- 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_; }
+ 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_); }
- const Ch* GetString() const { return uri_.c_str(); }
- SizeType GetStringLength() const { return static_cast<SizeType>(uri_.length()); }
+#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
- const Ch* GetBaseString() const { return base_.c_str(); }
- SizeType GetBaseStringLength() const { return static_cast<SizeType>(base_.length()); }
+ //! Equality operators
+ bool operator==(const GenericUri& rhs) const {
+ return Match(rhs, true);
+ }
- const Ch* GetFragString() const { return frag_.c_str(); }
- SizeType GetFragStringLength() const { return static_cast<SizeType>(frag_.length()); }
+ bool operator!=(const GenericUri& rhs) const {
+ return !Match(rhs, true);
+ }
- // Resolve this URI against another URI in accordance with URI resolution rules at
- // https://tools.ietf.org/html/rfc3986
+ bool Match(const GenericUri& uri, bool full) const {
+ 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;
+ }
+
+ //! 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.
- // This URI is updated in place where needed from the base URI.
- GenericUri& Resolve(const GenericUri& uri) {
- if (!scheme_.empty()) {
+ // 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
- RemoveDotSegments(path_);
+ 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 {
- if (!auth_.empty()) {
- RemoveDotSegments(path_);
+ // 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 {
- if (path_.empty()) {
- path_ = uri.GetPath();
- if (query_.empty()) {
- query_ = uri.GetQuery();
+ // 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 {
- static const String slash = GetSlashString().GetString();
- if (path_.find(slash) == 0) {
- // Absolute path - replace all the path
- RemoveDotSegments(path_);
+ if (path_[0] == '/') {
+ // Absolute path - use all of this path
+ resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength());
+ resuri.RemoveDotSegments();
} 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_);
+ // 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());
}
- auth_ = uri.GetAuth();
}
- scheme_ = uri.GetScheme();
}
- base_ = scheme_ + auth_ + path_ + query_;
- uri_ = base_ + frag_;
- return *this;
- }
+ // 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;
- bool Match(const GenericUri& uri, bool full) const {
- if (full)
- return uri_ == uri.Get();
- else
- return base_ == uri.GetBase();
+ // 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;
}
- // 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;\
- }
+ //! Get the allocator of this GenericUri.
+ Allocator& GetAllocator() { return *allocator_; }
+
+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)();
- RAPIDJSON_STRING_(SchemeEnd, ':')
- RAPIDJSON_STRING_(AuthStart, '/', '/')
- RAPIDJSON_STRING_(QueryStart, '?')
- RAPIDJSON_STRING_(FragStart, '#')
- RAPIDJSON_STRING_(Slash, '/')
- RAPIDJSON_STRING_(Dot, '.')
+ // 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;
+ }
-#undef RAPIDJSON_STRING_
+ // Free memory for a URI
+ void Free() {
+ //std::cout << "Free" << std::endl;
+ if (scheme_) {
+ //std::cout << " Freeing" << std::endl;
+ Allocator::Free(scheme_);
+ scheme_ = 0;
+ }
+ }
-private:
- // Parse a URI into constituent scheme, authority, path, query, fragment
+ // 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;
+ }
+
+ // 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 a path
+ // 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
+ // 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++;
+ }
+ // 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::size_t pos1 = path.find_last_of(slash);
- // Make sure we don't go beyond the start
- if (pos1 != std::string::npos && pos1 != 0) {
+ //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
- pos1 = path.find_last_of(slash, pos1 - 1);
- path = path.substr(0, pos1 + 1);
+ 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 {
- // Copy segment and add slash if not at end
- path += seg;
- if (pos != std::string::npos) path += slash;
+ // 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 if not at end
- if (pos != std::string::npos) temp = temp.substr(pos + 1);
+ // 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
diff --git a/test/unittest/pointertest.cpp b/test/unittest/pointertest.cpp
index a835af42..c00658c3 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<Value::Ch> 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
-
- EXPECT_TRUE((Pointer("/abc").GetUri(d, Pointer::UriType(doc)).Get()) == Pointer::UriType().Get()); // Out of boundary
+ Pointer::UriType doc("http://doc");
+ Pointer::UriType root("http://doc/root/");
+ Pointer::UriType empty = Pointer::UriType();
+
+ 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, 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 b5eda295..936b831e 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<Value::Ch> String;
- typedef GenericUri<Value, MemoryPoolAllocator<> > UriType;
- MemoryPoolAllocator<CrtAllocator> allocator;
-
- 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());
-
- 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());
-
- 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() == "");
-
- 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() == "");
-
- 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
- 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() == "");
-
- // 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() == "");
-
- 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);
-
- 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);
-
- 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);
+typedef GenericUri<Value, MemoryPoolAllocator<> > UriType;
+MemoryPoolAllocator<CrtAllocator> 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());
+
+#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
+
+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("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);
+
+// 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);
+
+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<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;
-
- // 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");
-
- 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 = 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 = 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 = 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 = 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(String("file.json"));
- ref = UriType(String("newfile.json"));
- EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json");
-
- base = UriType(String("file.json"));
- ref = UriType(String("./newfile.json"));
- EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json");
-
- base = UriType(String("file.json"));
- ref = UriType(String("parent/../newfile.json"));
- EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json");
-
- base = UriType(String("file.json"));
- ref = UriType(String("parent/./newfile.json"));
- EXPECT_TRUE(ref.Resolve(base).Get() == "parent/newfile.json");
-
- base = UriType(String("file.json"));
- ref = UriType(String("../../parent/.././newfile.json"));
- EXPECT_TRUE(ref.Resolve(base).Get() == "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 = UriType(String("#frag/stuff"));
- ref = UriType(String("#newfrag/newstuff"));
- EXPECT_TRUE(ref.Resolve(base).Get() == "#newfrag/newstuff");
-
- // test ref fragment always wins
- base = UriType(String("/path#frag"));
- ref = UriType(String(""));
- EXPECT_TRUE(ref.Resolve(base).Get() == "/path");
-
- // 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");
+typedef GenericUri<Value> 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);
+
+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 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);
+
+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", &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(), "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);
+
+// 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);
+
+// 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);
+}
+
+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__)