Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-02-21 10:13:05

0001 // Tencent is pleased to support the open source community by making RapidJSON available.
0002 //
0003 // (C) Copyright IBM Corporation 2021
0004 //
0005 // Licensed under the MIT License (the "License"); you may not use this file except
0006 // in compliance with the License. You may obtain a copy of the License at
0007 //
0008 // http://opensource.org/licenses/MIT
0009 //
0010 // Unless required by applicable law or agreed to in writing, software distributed
0011 // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
0012 // CONDITIONS OF ANY KIND, either express or implied. See the License for the
0013 // specific language governing permissions and limitations under the License.
0014 
0015 #ifndef RAPIDJSON_URI_H_
0016 #define RAPIDJSON_URI_H_
0017 
0018 #include "internal/strfunc.h"
0019 
0020 #if defined(__clang__)
0021 RAPIDJSON_DIAG_PUSH
0022 RAPIDJSON_DIAG_OFF(c++98-compat)
0023 #elif defined(_MSC_VER)
0024 RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
0025 #endif
0026 
0027 RAPIDJSON_NAMESPACE_BEGIN
0028 
0029 ///////////////////////////////////////////////////////////////////////////////
0030 // GenericUri
0031 
0032 template <typename ValueType, typename Allocator=CrtAllocator>
0033 class GenericUri {
0034 public:
0035     typedef typename ValueType::Ch Ch;
0036 #if RAPIDJSON_HAS_STDSTRING
0037     typedef std::basic_string<Ch> String;
0038 #endif
0039 
0040     //! Constructors
0041     GenericUri(Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
0042     }
0043 
0044     GenericUri(const Ch* uri, SizeType len, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
0045         Parse(uri, len);
0046     }
0047 
0048     GenericUri(const Ch* uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
0049         Parse(uri, internal::StrLen<Ch>(uri));
0050     }
0051 
0052     // Use with specializations of GenericValue
0053     template<typename T> GenericUri(const T& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
0054         const Ch* u = uri.template Get<const Ch*>(); // TypeHelper from document.h
0055         Parse(u, internal::StrLen<Ch>(u));
0056     }
0057 
0058 #if RAPIDJSON_HAS_STDSTRING
0059     GenericUri(const String& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
0060         Parse(uri.c_str(), internal::StrLen<Ch>(uri.c_str()));
0061     }
0062 #endif
0063 
0064     //! Copy constructor
0065     GenericUri(const GenericUri& rhs) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(), ownAllocator_() {
0066         *this = rhs;
0067     }
0068 
0069     //! Copy constructor
0070     GenericUri(const GenericUri& rhs, Allocator* allocator) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
0071         *this = rhs;
0072     }
0073 
0074     //! Destructor.
0075     ~GenericUri() {
0076         Free();
0077         RAPIDJSON_DELETE(ownAllocator_);
0078     }
0079 
0080     //! Assignment operator
0081     GenericUri& operator=(const GenericUri& rhs) {
0082         if (this != &rhs) {
0083             // Do not delete ownAllocator
0084             Free();
0085             Allocate(rhs.GetStringLength());
0086             auth_ = CopyPart(scheme_, rhs.scheme_, rhs.GetSchemeStringLength());
0087             path_ = CopyPart(auth_, rhs.auth_, rhs.GetAuthStringLength());
0088             query_ = CopyPart(path_, rhs.path_, rhs.GetPathStringLength());
0089             frag_ = CopyPart(query_, rhs.query_, rhs.GetQueryStringLength());
0090             base_ = CopyPart(frag_, rhs.frag_, rhs.GetFragStringLength());
0091             uri_ = CopyPart(base_, rhs.base_, rhs.GetBaseStringLength());
0092             CopyPart(uri_, rhs.uri_, rhs.GetStringLength());
0093         }
0094         return *this;
0095     }
0096 
0097     //! Getters
0098     // Use with specializations of GenericValue
0099     template<typename T> void Get(T& uri, Allocator& allocator) {
0100         uri.template Set<const Ch*>(this->GetString(), allocator); // TypeHelper from document.h
0101     }
0102 
0103     const Ch* GetString() const { return uri_; }
0104     SizeType GetStringLength() const { return uri_ == 0 ? 0 : internal::StrLen<Ch>(uri_); }
0105     const Ch* GetBaseString() const { return base_; }
0106     SizeType GetBaseStringLength() const { return base_ == 0 ? 0 : internal::StrLen<Ch>(base_); }
0107     const Ch* GetSchemeString() const { return scheme_; }
0108     SizeType GetSchemeStringLength() const { return scheme_ == 0 ? 0 : internal::StrLen<Ch>(scheme_); }
0109     const Ch* GetAuthString() const { return auth_; }
0110     SizeType GetAuthStringLength() const { return auth_ == 0 ? 0 : internal::StrLen<Ch>(auth_); }
0111     const Ch* GetPathString() const { return path_; }
0112     SizeType GetPathStringLength() const { return path_ == 0 ? 0 : internal::StrLen<Ch>(path_); }
0113     const Ch* GetQueryString() const { return query_; }
0114     SizeType GetQueryStringLength() const { return query_ == 0 ? 0 : internal::StrLen<Ch>(query_); }
0115     const Ch* GetFragString() const { return frag_; }
0116     SizeType GetFragStringLength() const { return frag_ == 0 ? 0 : internal::StrLen<Ch>(frag_); }
0117 
0118 #if RAPIDJSON_HAS_STDSTRING
0119     static String Get(const GenericUri& uri) { return String(uri.GetString(), uri.GetStringLength()); }
0120     static String GetBase(const GenericUri& uri) { return String(uri.GetBaseString(), uri.GetBaseStringLength()); }
0121     static String GetScheme(const GenericUri& uri) { return String(uri.GetSchemeString(), uri.GetSchemeStringLength()); }
0122     static String GetAuth(const GenericUri& uri) { return String(uri.GetAuthString(), uri.GetAuthStringLength()); }
0123     static String GetPath(const GenericUri& uri) { return String(uri.GetPathString(), uri.GetPathStringLength()); }
0124     static String GetQuery(const GenericUri& uri) { return String(uri.GetQueryString(), uri.GetQueryStringLength()); }
0125     static String GetFrag(const GenericUri& uri) { return String(uri.GetFragString(), uri.GetFragStringLength()); }
0126 #endif
0127 
0128     //! Equality operators
0129     bool operator==(const GenericUri& rhs) const {
0130         return Match(rhs, true);
0131     }
0132 
0133     bool operator!=(const GenericUri& rhs) const {
0134         return !Match(rhs, true);
0135     }
0136 
0137     bool Match(const GenericUri& uri, bool full = true) const {
0138         Ch* s1;
0139         Ch* s2;
0140         if (full) {
0141             s1 = uri_;
0142             s2 = uri.uri_;
0143         } else {
0144             s1 = base_;
0145             s2 = uri.base_;
0146         }
0147         if (s1 == s2) return true;
0148         if (s1 == 0 || s2 == 0) return false;
0149         return internal::StrCmp<Ch>(s1, s2) == 0;
0150     }
0151 
0152     //! Resolve this URI against another (base) URI in accordance with URI resolution rules.
0153     // See https://tools.ietf.org/html/rfc3986
0154     // Use for resolving an id or $ref with an in-scope id.
0155     // Returns a new GenericUri for the resolved URI.
0156     GenericUri Resolve(const GenericUri& baseuri, Allocator* allocator = 0) {
0157         GenericUri resuri;
0158         resuri.allocator_ = allocator;
0159         // Ensure enough space for combining paths
0160         resuri.Allocate(GetStringLength() + baseuri.GetStringLength() + 1); // + 1 for joining slash
0161 
0162         if (!(GetSchemeStringLength() == 0)) {
0163             // Use all of this URI
0164             resuri.auth_ = CopyPart(resuri.scheme_, scheme_, GetSchemeStringLength());
0165             resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength());
0166             resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength());
0167             resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
0168             resuri.RemoveDotSegments();
0169         } else {
0170             // Use the base scheme
0171             resuri.auth_ = CopyPart(resuri.scheme_, baseuri.scheme_, baseuri.GetSchemeStringLength());
0172             if (!(GetAuthStringLength() == 0)) {
0173                 // Use this auth, path, query
0174                 resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength());
0175                 resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength());
0176                 resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
0177                 resuri.RemoveDotSegments();
0178             } else {
0179                 // Use the base auth
0180                 resuri.path_ = CopyPart(resuri.auth_, baseuri.auth_, baseuri.GetAuthStringLength());
0181                 if (GetPathStringLength() == 0) {
0182                     // Use the base path
0183                     resuri.query_ = CopyPart(resuri.path_, baseuri.path_, baseuri.GetPathStringLength());
0184                     if (GetQueryStringLength() == 0) {
0185                         // Use the base query
0186                         resuri.frag_ = CopyPart(resuri.query_, baseuri.query_, baseuri.GetQueryStringLength());
0187                     } else {
0188                         // Use this query
0189                         resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
0190                     }
0191                 } else {
0192                     if (path_[0] == '/') {
0193                         // Absolute path - use all of this path
0194                         resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength());
0195                         resuri.RemoveDotSegments();
0196                     } else {
0197                         // Relative path - append this path to base path after base path's last slash
0198                         size_t pos = 0;
0199                         if (!(baseuri.GetAuthStringLength() == 0) && baseuri.GetPathStringLength() == 0) {
0200                             resuri.path_[pos] = '/';
0201                             pos++;
0202                         }
0203                         size_t lastslashpos = baseuri.GetPathStringLength();
0204                         while (lastslashpos > 0) {
0205                             if (baseuri.path_[lastslashpos - 1] == '/') break;
0206                             lastslashpos--;
0207                         }
0208                         std::memcpy(&resuri.path_[pos], baseuri.path_, lastslashpos * sizeof(Ch));
0209                         pos += lastslashpos;
0210                         resuri.query_ = CopyPart(&resuri.path_[pos], path_, GetPathStringLength());
0211                         resuri.RemoveDotSegments();
0212                     }
0213                     // Use this query
0214                     resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
0215                 }
0216             }
0217         }
0218         // Always use this frag
0219         resuri.base_ = CopyPart(resuri.frag_, frag_, GetFragStringLength());
0220 
0221         // Re-constitute base_ and uri_
0222         resuri.SetBase();
0223         resuri.uri_ = resuri.base_ + resuri.GetBaseStringLength() + 1;
0224         resuri.SetUri();
0225         return resuri;
0226     }
0227 
0228     //! Get the allocator of this GenericUri.
0229     Allocator& GetAllocator() { return *allocator_; }
0230 
0231 private:
0232     // Allocate memory for a URI
0233     // Returns total amount allocated
0234     std::size_t Allocate(std::size_t len) {
0235         // Create own allocator if user did not supply.
0236         if (!allocator_)
0237             ownAllocator_ =  allocator_ = RAPIDJSON_NEW(Allocator)();
0238 
0239         // Allocate one block containing each part of the URI (5) plus base plus full URI, all null terminated.
0240         // Order: scheme, auth, path, query, frag, base, uri
0241         // Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
0242         size_t total = (3 * len + 7) * sizeof(Ch);
0243         scheme_ = static_cast<Ch*>(allocator_->Malloc(total));
0244         *scheme_ = '\0';
0245         auth_ = scheme_;
0246         auth_++;
0247         *auth_ = '\0';
0248         path_ = auth_;
0249         path_++;
0250         *path_ = '\0';
0251         query_ = path_;
0252         query_++;
0253         *query_ = '\0';
0254         frag_ = query_;
0255         frag_++;
0256         *frag_ = '\0';
0257         base_ = frag_;
0258         base_++;
0259         *base_ = '\0';
0260         uri_ = base_;
0261         uri_++;
0262         *uri_ = '\0';
0263         return total;
0264     }
0265 
0266     // Free memory for a URI
0267     void Free() {
0268         if (scheme_) {
0269             Allocator::Free(scheme_);
0270             scheme_ = 0;
0271         }
0272     }
0273 
0274     // Parse a URI into constituent scheme, authority, path, query, & fragment parts
0275     // Supports URIs that match regex ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? as per
0276     // https://tools.ietf.org/html/rfc3986
0277     void Parse(const Ch* uri, std::size_t len) {
0278         std::size_t start = 0, pos1 = 0, pos2 = 0;
0279         Allocate(len);
0280 
0281         // Look for scheme ([^:/?#]+):)?
0282         if (start < len) {
0283             while (pos1 < len) {
0284                 if (uri[pos1] == ':') break;
0285                 pos1++;
0286             }
0287             if (pos1 != len) {
0288                 while (pos2 < len) {
0289                     if (uri[pos2] == '/') break;
0290                     if (uri[pos2] == '?') break;
0291                     if (uri[pos2] == '#') break;
0292                     pos2++;
0293                 }
0294                 if (pos1 < pos2) {
0295                     pos1++;
0296                     std::memcpy(scheme_, &uri[start], pos1 * sizeof(Ch));
0297                     scheme_[pos1] = '\0';
0298                     start = pos1;
0299                 }
0300             }
0301         }
0302         // Look for auth (//([^/?#]*))?
0303         // Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
0304         auth_ = scheme_ + GetSchemeStringLength();
0305         auth_++;
0306         *auth_ = '\0';
0307         if (start < len - 1 && uri[start] == '/' && uri[start + 1] == '/') {
0308             pos2 = start + 2;
0309             while (pos2 < len) {
0310                 if (uri[pos2] == '/') break;
0311                 if (uri[pos2] == '?') break;
0312                 if (uri[pos2] == '#') break;
0313                 pos2++;
0314             }
0315             std::memcpy(auth_, &uri[start], (pos2 - start) * sizeof(Ch));
0316             auth_[pos2 - start] = '\0';
0317             start = pos2;
0318         }
0319         // Look for path ([^?#]*)
0320         // Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
0321         path_ = auth_ + GetAuthStringLength();
0322         path_++;
0323         *path_ = '\0';
0324         if (start < len) {
0325             pos2 = start;
0326             while (pos2 < len) {
0327                 if (uri[pos2] == '?') break;
0328                 if (uri[pos2] == '#') break;
0329                 pos2++;
0330             }
0331             if (start != pos2) {
0332                 std::memcpy(path_, &uri[start], (pos2 - start) * sizeof(Ch));
0333                 path_[pos2 - start] = '\0';
0334                 if (path_[0] == '/')
0335                     RemoveDotSegments();   // absolute path - normalize
0336                 start = pos2;
0337             }
0338         }
0339         // Look for query (\?([^#]*))?
0340         // Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
0341         query_ = path_ + GetPathStringLength();
0342         query_++;
0343         *query_ = '\0';
0344         if (start < len && uri[start] == '?') {
0345             pos2 = start + 1;
0346             while (pos2 < len) {
0347                 if (uri[pos2] == '#') break;
0348                 pos2++;
0349             }
0350             if (start != pos2) {
0351                 std::memcpy(query_, &uri[start], (pos2 - start) * sizeof(Ch));
0352                 query_[pos2 - start] = '\0';
0353                 start = pos2;
0354             }
0355         }
0356         // Look for fragment (#(.*))?
0357         // Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
0358         frag_ = query_ + GetQueryStringLength();
0359         frag_++;
0360         *frag_ = '\0';
0361         if (start < len && uri[start] == '#') {
0362             std::memcpy(frag_, &uri[start], (len - start) * sizeof(Ch));
0363             frag_[len - start] = '\0';
0364         }
0365 
0366         // Re-constitute base_ and uri_
0367         base_ = frag_ + GetFragStringLength() + 1;
0368         SetBase();
0369         uri_ = base_ + GetBaseStringLength() + 1;
0370         SetUri();
0371     }
0372 
0373     // Reconstitute base
0374     void SetBase() {
0375         Ch* next = base_;
0376         std::memcpy(next, scheme_, GetSchemeStringLength() * sizeof(Ch));
0377         next+= GetSchemeStringLength();
0378         std::memcpy(next, auth_, GetAuthStringLength() * sizeof(Ch));
0379         next+= GetAuthStringLength();
0380         std::memcpy(next, path_, GetPathStringLength() * sizeof(Ch));
0381         next+= GetPathStringLength();
0382         std::memcpy(next, query_, GetQueryStringLength() * sizeof(Ch));
0383         next+= GetQueryStringLength();
0384         *next = '\0';
0385     }
0386 
0387     // Reconstitute uri
0388     void SetUri() {
0389         Ch* next = uri_;
0390         std::memcpy(next, base_, GetBaseStringLength() * sizeof(Ch));
0391         next+= GetBaseStringLength();
0392         std::memcpy(next, frag_, GetFragStringLength() * sizeof(Ch));
0393         next+= GetFragStringLength();
0394         *next = '\0';
0395     }
0396 
0397     // Copy a part from one GenericUri to another
0398     // Return the pointer to the next part to be copied to
0399     Ch* CopyPart(Ch* to, Ch* from, std::size_t len) {
0400         RAPIDJSON_ASSERT(to != 0);
0401         RAPIDJSON_ASSERT(from != 0);
0402         std::memcpy(to, from, len * sizeof(Ch));
0403         to[len] = '\0';
0404         Ch* next = to + len + 1;
0405         return next;
0406     }
0407 
0408     // Remove . and .. segments from the path_ member.
0409     // https://tools.ietf.org/html/rfc3986
0410     // This is done in place as we are only removing segments.
0411     void RemoveDotSegments() {
0412         std::size_t pathlen = GetPathStringLength();
0413         std::size_t pathpos = 0;  // Position in path_
0414         std::size_t newpos = 0;   // Position in new path_
0415 
0416         // Loop through each segment in original path_
0417         while (pathpos < pathlen) {
0418             // Get next segment, bounded by '/' or end
0419             size_t slashpos = 0;
0420             while ((pathpos + slashpos) < pathlen) {
0421                 if (path_[pathpos + slashpos] == '/') break;
0422                 slashpos++;
0423             }
0424             // Check for .. and . segments
0425             if (slashpos == 2 && path_[pathpos] == '.' && path_[pathpos + 1] == '.') {
0426                 // Backup a .. segment in the new path_
0427                 // We expect to find a previously added slash at the end or nothing
0428                 RAPIDJSON_ASSERT(newpos == 0 || path_[newpos - 1] == '/');
0429                 size_t lastslashpos = newpos;
0430                 // Make sure we don't go beyond the start segment
0431                 if (lastslashpos > 1) {
0432                     // Find the next to last slash and back up to it
0433                     lastslashpos--;
0434                     while (lastslashpos > 0) {
0435                         if (path_[lastslashpos - 1] == '/') break;
0436                         lastslashpos--;
0437                     }
0438                     // Set the new path_ position
0439                     newpos = lastslashpos;
0440                 }
0441             } else if (slashpos == 1 && path_[pathpos] == '.') {
0442                 // Discard . segment, leaves new path_ unchanged
0443             } else {
0444                 // Move any other kind of segment to the new path_
0445                 RAPIDJSON_ASSERT(newpos <= pathpos);
0446                 std::memmove(&path_[newpos], &path_[pathpos], slashpos * sizeof(Ch));
0447                 newpos += slashpos;
0448                 // Add slash if not at end
0449                 if ((pathpos + slashpos) < pathlen) {
0450                     path_[newpos] = '/';
0451                     newpos++;
0452                 }
0453             }
0454             // Move to next segment
0455             pathpos += slashpos + 1;
0456         }
0457         path_[newpos] = '\0';
0458     }
0459 
0460     Ch* uri_;    // Everything
0461     Ch* base_;   // Everything except fragment
0462     Ch* scheme_; // Includes the :
0463     Ch* auth_;   // Includes the //
0464     Ch* path_;   // Absolute if starts with /
0465     Ch* query_;  // Includes the ?
0466     Ch* frag_;   // Includes the #
0467 
0468     Allocator* allocator_;      //!< The current allocator. It is either user-supplied or equal to ownAllocator_.
0469     Allocator* ownAllocator_;   //!< Allocator owned by this Uri.
0470 };
0471 
0472 //! GenericUri for Value (UTF-8, default allocator).
0473 typedef GenericUri<Value> Uri;
0474 
0475 RAPIDJSON_NAMESPACE_END
0476 
0477 #if defined(__clang__)
0478 RAPIDJSON_DIAG_POP
0479 #endif
0480 
0481 #endif // RAPIDJSON_URI_H_