Arcane  v3.16.8.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
uri.h
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2// Tencent is pleased to support the open source community by making RapidJSON available.
3//
4// (C) Copyright IBM Corporation 2021
5//
6// Licensed under the MIT License (the "License"); you may not use this file except
7// in compliance with the License. You may obtain a copy of the License at
8//
9// http://opensource.org/licenses/MIT
10//
11// Unless required by applicable law or agreed to in writing, software distributed
12// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
13// CONDITIONS OF ANY KIND, either express or implied. See the License for the
14// specific language governing permissions and limitations under the License.
15
16#ifndef RAPIDJSON_URI_H_
17#define RAPIDJSON_URI_H_
18
19#include "internal/strfunc.h"
20
21#if defined(__clang__)
22RAPIDJSON_DIAG_PUSH
23RAPIDJSON_DIAG_OFF(c++98-compat)
24#elif defined(_MSC_VER)
25RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
26#endif
27
29
31// GenericUri
32
33template <typename ValueType, typename Allocator=CrtAllocator>
35public:
36 typedef typename ValueType::Ch Ch;
37#if RAPIDJSON_HAS_STDSTRING
38 typedef std::basic_string<Ch> String;
39#endif
40
42 GenericUri(Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
43 }
44
45 GenericUri(const Ch* uri, SizeType len, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
46 Parse(uri, len);
47 }
48
49 GenericUri(const Ch* uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
50 Parse(uri, internal::StrLen<Ch>(uri));
51 }
52
53 // Use with specializations of GenericValue
54 template<typename T> GenericUri(const T& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
55 const Ch* u = uri.template Get<const Ch*>(); // TypeHelper from document.h
56 Parse(u, internal::StrLen<Ch>(u));
57 }
58
59#if RAPIDJSON_HAS_STDSTRING
60 GenericUri(const String& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
61 Parse(uri.c_str(), internal::StrLen<Ch>(uri.c_str()));
62 }
63#endif
64
66 GenericUri(const GenericUri& rhs) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(), ownAllocator_() {
67 *this = rhs;
68 }
69
71 GenericUri(const GenericUri& rhs, Allocator* allocator) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
72 *this = rhs;
73 }
74
77 Free();
79 }
80
83 if (this != &rhs) {
84 // Do not delete ownAllocator
85 Free();
86 Allocate(rhs.GetStringLength());
87 auth_ = CopyPart(scheme_, rhs.scheme_, rhs.GetSchemeStringLength());
88 path_ = CopyPart(auth_, rhs.auth_, rhs.GetAuthStringLength());
89 query_ = CopyPart(path_, rhs.path_, rhs.GetPathStringLength());
90 frag_ = CopyPart(query_, rhs.query_, rhs.GetQueryStringLength());
91 base_ = CopyPart(frag_, rhs.frag_, rhs.GetFragStringLength());
92 uri_ = CopyPart(base_, rhs.base_, rhs.GetBaseStringLength());
93 CopyPart(uri_, rhs.uri_, rhs.GetStringLength());
94 }
95 return *this;
96 }
97
99 // Use with specializations of GenericValue
100 template<typename T> void Get(T& uri, Allocator& allocator) {
101 uri.template Set<const Ch*>(this->GetString(), allocator); // TypeHelper from document.h
102 }
103
104 const Ch* GetString() const { return uri_; }
105 SizeType GetStringLength() const { return uri_ == 0 ? 0 : internal::StrLen<Ch>(uri_); }
106 const Ch* GetBaseString() const { return base_; }
107 SizeType GetBaseStringLength() const { return base_ == 0 ? 0 : internal::StrLen<Ch>(base_); }
108 const Ch* GetSchemeString() const { return scheme_; }
109 SizeType GetSchemeStringLength() const { return scheme_ == 0 ? 0 : internal::StrLen<Ch>(scheme_); }
110 const Ch* GetAuthString() const { return auth_; }
111 SizeType GetAuthStringLength() const { return auth_ == 0 ? 0 : internal::StrLen<Ch>(auth_); }
112 const Ch* GetPathString() const { return path_; }
113 SizeType GetPathStringLength() const { return path_ == 0 ? 0 : internal::StrLen<Ch>(path_); }
114 const Ch* GetQueryString() const { return query_; }
115 SizeType GetQueryStringLength() const { return query_ == 0 ? 0 : internal::StrLen<Ch>(query_); }
116 const Ch* GetFragString() const { return frag_; }
117 SizeType GetFragStringLength() const { return frag_ == 0 ? 0 : internal::StrLen<Ch>(frag_); }
118
119#if RAPIDJSON_HAS_STDSTRING
120 static String Get(const GenericUri& uri) { return String(uri.GetString(), uri.GetStringLength()); }
121 static String GetBase(const GenericUri& uri) { return String(uri.GetBaseString(), uri.GetBaseStringLength()); }
122 static String GetScheme(const GenericUri& uri) { return String(uri.GetSchemeString(), uri.GetSchemeStringLength()); }
123 static String GetAuth(const GenericUri& uri) { return String(uri.GetAuthString(), uri.GetAuthStringLength()); }
124 static String GetPath(const GenericUri& uri) { return String(uri.GetPathString(), uri.GetPathStringLength()); }
125 static String GetQuery(const GenericUri& uri) { return String(uri.GetQueryString(), uri.GetQueryStringLength()); }
126 static String GetFrag(const GenericUri& uri) { return String(uri.GetFragString(), uri.GetFragStringLength()); }
127#endif
128
130 bool operator==(const GenericUri& rhs) const {
131 return Match(rhs, true);
132 }
133
134 bool operator!=(const GenericUri& rhs) const {
135 return !Match(rhs, true);
136 }
137
138 bool Match(const GenericUri& uri, bool full = true) const {
139 Ch* s1;
140 Ch* s2;
141 if (full) {
142 s1 = uri_;
143 s2 = uri.uri_;
144 } else {
145 s1 = base_;
146 s2 = uri.base_;
147 }
148 if (s1 == s2) return true;
149 if (s1 == 0 || s2 == 0) return false;
150 return internal::StrCmp<Ch>(s1, s2) == 0;
151 }
152
154 // See https://tools.ietf.org/html/rfc3986
155 // Use for resolving an id or $ref with an in-scope id.
156 // Returns a new GenericUri for the resolved URI.
157 GenericUri Resolve(const GenericUri& baseuri, Allocator* allocator = 0) {
158 GenericUri resuri;
159 resuri.allocator_ = allocator;
160 // Ensure enough space for combining paths
161 resuri.Allocate(GetStringLength() + baseuri.GetStringLength() + 1); // + 1 for joining slash
162
163 if (!(GetSchemeStringLength() == 0)) {
164 // Use all of this URI
165 resuri.auth_ = CopyPart(resuri.scheme_, scheme_, GetSchemeStringLength());
166 resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength());
167 resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength());
168 resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
169 resuri.RemoveDotSegments();
170 } else {
171 // Use the base scheme
172 resuri.auth_ = CopyPart(resuri.scheme_, baseuri.scheme_, baseuri.GetSchemeStringLength());
173 if (!(GetAuthStringLength() == 0)) {
174 // Use this auth, path, query
175 resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength());
176 resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength());
177 resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
178 resuri.RemoveDotSegments();
179 } else {
180 // Use the base auth
181 resuri.path_ = CopyPart(resuri.auth_, baseuri.auth_, baseuri.GetAuthStringLength());
182 if (GetPathStringLength() == 0) {
183 // Use the base path
184 resuri.query_ = CopyPart(resuri.path_, baseuri.path_, baseuri.GetPathStringLength());
185 if (GetQueryStringLength() == 0) {
186 // Use the base query
187 resuri.frag_ = CopyPart(resuri.query_, baseuri.query_, baseuri.GetQueryStringLength());
188 } else {
189 // Use this query
190 resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
191 }
192 } else {
193 if (path_[0] == '/') {
194 // Absolute path - use all of this path
195 resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength());
196 resuri.RemoveDotSegments();
197 } else {
198 // Relative path - append this path to base path after base path's last slash
199 size_t pos = 0;
200 if (!(baseuri.GetAuthStringLength() == 0) && baseuri.GetPathStringLength() == 0) {
201 resuri.path_[pos] = '/';
202 pos++;
203 }
204 size_t lastslashpos = baseuri.GetPathStringLength();
205 while (lastslashpos > 0) {
206 if (baseuri.path_[lastslashpos - 1] == '/') break;
207 lastslashpos--;
208 }
209 std::memcpy(&resuri.path_[pos], baseuri.path_, lastslashpos * sizeof(Ch));
210 pos += lastslashpos;
211 resuri.query_ = CopyPart(&resuri.path_[pos], path_, GetPathStringLength());
212 resuri.RemoveDotSegments();
213 }
214 // Use this query
215 resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
216 }
217 }
218 }
219 // Always use this frag
220 resuri.base_ = CopyPart(resuri.frag_, frag_, GetFragStringLength());
221
222 // Re-constitute base_ and uri_
223 resuri.SetBase();
224 resuri.uri_ = resuri.base_ + resuri.GetBaseStringLength() + 1;
225 resuri.SetUri();
226 return resuri;
227 }
228
231
232private:
233 // Allocate memory for a URI
234 // Returns total amount allocated
235 std::size_t Allocate(std::size_t len) {
236 // Create own allocator if user did not supply.
237 if (!allocator_)
239
240 // Allocate one block containing each part of the URI (5) plus base plus full URI, all null terminated.
241 // Order: scheme, auth, path, query, frag, base, uri
242 // Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
243 size_t total = (3 * len + 7) * sizeof(Ch);
244 scheme_ = static_cast<Ch*>(allocator_->Malloc(total));
245 *scheme_ = '\0';
246 auth_ = scheme_;
247 auth_++;
248 *auth_ = '\0';
249 path_ = auth_;
250 path_++;
251 *path_ = '\0';
252 query_ = path_;
253 query_++;
254 *query_ = '\0';
255 frag_ = query_;
256 frag_++;
257 *frag_ = '\0';
258 base_ = frag_;
259 base_++;
260 *base_ = '\0';
261 uri_ = base_;
262 uri_++;
263 *uri_ = '\0';
264 return total;
265 }
266
267 // Free memory for a URI
268 void Free() {
269 if (scheme_) {
270 Allocator::Free(scheme_);
271 scheme_ = 0;
272 }
273 }
274
275 // Parse a URI into constituent scheme, authority, path, query, & fragment parts
276 // Supports URIs that match regex ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? as per
277 // https://tools.ietf.org/html/rfc3986
278 void Parse(const Ch* uri, std::size_t len) {
279 std::size_t start = 0, pos1 = 0, pos2 = 0;
280 Allocate(len);
281
282 // Look for scheme ([^:/?#]+):)?
283 if (start < len) {
284 while (pos1 < len) {
285 if (uri[pos1] == ':') break;
286 pos1++;
287 }
288 if (pos1 != len) {
289 while (pos2 < len) {
290 if (uri[pos2] == '/') break;
291 if (uri[pos2] == '?') break;
292 if (uri[pos2] == '#') break;
293 pos2++;
294 }
295 if (pos1 < pos2) {
296 pos1++;
297 std::memcpy(scheme_, &uri[start], pos1 * sizeof(Ch));
298 scheme_[pos1] = '\0';
299 start = pos1;
300 }
301 }
302 }
303 // Look for auth (//([^/?#]*))?
304 // Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
305 auth_ = scheme_ + GetSchemeStringLength();
306 auth_++;
307 *auth_ = '\0';
308 if (start < len - 1 && uri[start] == '/' && uri[start + 1] == '/') {
309 pos2 = start + 2;
310 while (pos2 < len) {
311 if (uri[pos2] == '/') break;
312 if (uri[pos2] == '?') break;
313 if (uri[pos2] == '#') break;
314 pos2++;
315 }
316 std::memcpy(auth_, &uri[start], (pos2 - start) * sizeof(Ch));
317 auth_[pos2 - start] = '\0';
318 start = pos2;
319 }
320 // Look for path ([^?#]*)
321 // Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
322 path_ = auth_ + GetAuthStringLength();
323 path_++;
324 *path_ = '\0';
325 if (start < len) {
326 pos2 = start;
327 while (pos2 < len) {
328 if (uri[pos2] == '?') break;
329 if (uri[pos2] == '#') break;
330 pos2++;
331 }
332 if (start != pos2) {
333 std::memcpy(path_, &uri[start], (pos2 - start) * sizeof(Ch));
334 path_[pos2 - start] = '\0';
335 if (path_[0] == '/')
336 RemoveDotSegments(); // absolute path - normalize
337 start = pos2;
338 }
339 }
340 // Look for query (\?([^#]*))?
341 // Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
342 query_ = path_ + GetPathStringLength();
343 query_++;
344 *query_ = '\0';
345 if (start < len && uri[start] == '?') {
346 pos2 = start + 1;
347 while (pos2 < len) {
348 if (uri[pos2] == '#') break;
349 pos2++;
350 }
351 if (start != pos2) {
352 std::memcpy(query_, &uri[start], (pos2 - start) * sizeof(Ch));
353 query_[pos2 - start] = '\0';
354 start = pos2;
355 }
356 }
357 // Look for fragment (#(.*))?
358 // Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
359 frag_ = query_ + GetQueryStringLength();
360 frag_++;
361 *frag_ = '\0';
362 if (start < len && uri[start] == '#') {
363 std::memcpy(frag_, &uri[start], (len - start) * sizeof(Ch));
364 frag_[len - start] = '\0';
365 }
366
367 // Re-constitute base_ and uri_
368 base_ = frag_ + GetFragStringLength() + 1;
369 SetBase();
370 uri_ = base_ + GetBaseStringLength() + 1;
371 SetUri();
372 }
373
374 // Reconstitute base
375 void SetBase() {
376 Ch* next = base_;
377 std::memcpy(next, scheme_, GetSchemeStringLength() * sizeof(Ch));
378 next+= GetSchemeStringLength();
379 std::memcpy(next, auth_, GetAuthStringLength() * sizeof(Ch));
380 next+= GetAuthStringLength();
381 std::memcpy(next, path_, GetPathStringLength() * sizeof(Ch));
382 next+= GetPathStringLength();
383 std::memcpy(next, query_, GetQueryStringLength() * sizeof(Ch));
384 next+= GetQueryStringLength();
385 *next = '\0';
386 }
387
388 // Reconstitute uri
389 void SetUri() {
390 Ch* next = uri_;
391 std::memcpy(next, base_, GetBaseStringLength() * sizeof(Ch));
392 next+= GetBaseStringLength();
393 std::memcpy(next, frag_, GetFragStringLength() * sizeof(Ch));
394 next+= GetFragStringLength();
395 *next = '\0';
396 }
397
398 // Copy a part from one GenericUri to another
399 // Return the pointer to the next part to be copied to
400 Ch* CopyPart(Ch* to, Ch* from, std::size_t len) {
401 RAPIDJSON_ASSERT(to != 0);
402 RAPIDJSON_ASSERT(from != 0);
403 std::memcpy(to, from, len * sizeof(Ch));
404 to[len] = '\0';
405 Ch* next = to + len + 1;
406 return next;
407 }
408
409 // Remove . and .. segments from the path_ member.
410 // https://tools.ietf.org/html/rfc3986
411 // This is done in place as we are only removing segments.
412 void RemoveDotSegments() {
413 std::size_t pathlen = GetPathStringLength();
414 std::size_t pathpos = 0; // Position in path_
415 std::size_t newpos = 0; // Position in new path_
416
417 // Loop through each segment in original path_
418 while (pathpos < pathlen) {
419 // Get next segment, bounded by '/' or end
420 size_t slashpos = 0;
421 while ((pathpos + slashpos) < pathlen) {
422 if (path_[pathpos + slashpos] == '/') break;
423 slashpos++;
424 }
425 // Check for .. and . segments
426 if (slashpos == 2 && path_[pathpos] == '.' && path_[pathpos + 1] == '.') {
427 // Backup a .. segment in the new path_
428 // We expect to find a previously added slash at the end or nothing
429 RAPIDJSON_ASSERT(newpos == 0 || path_[newpos - 1] == '/');
430 size_t lastslashpos = newpos;
431 // Make sure we don't go beyond the start segment
432 if (lastslashpos > 1) {
433 // Find the next to last slash and back up to it
434 lastslashpos--;
435 while (lastslashpos > 0) {
436 if (path_[lastslashpos - 1] == '/') break;
437 lastslashpos--;
438 }
439 // Set the new path_ position
440 newpos = lastslashpos;
441 }
442 } else if (slashpos == 1 && path_[pathpos] == '.') {
443 // Discard . segment, leaves new path_ unchanged
444 } else {
445 // Move any other kind of segment to the new path_
446 RAPIDJSON_ASSERT(newpos <= pathpos);
447 std::memmove(&path_[newpos], &path_[pathpos], slashpos * sizeof(Ch));
448 newpos += slashpos;
449 // Add slash if not at end
450 if ((pathpos + slashpos) < pathlen) {
451 path_[newpos] = '/';
452 newpos++;
453 }
454 }
455 // Move to next segment
456 pathpos += slashpos + 1;
457 }
458 path_[newpos] = '\0';
459 }
460
461 Ch* uri_; // Everything
462 Ch* base_; // Everything except fragment
463 Ch* scheme_; // Includes the :
464 Ch* auth_; // Includes the //
465 Ch* path_; // Absolute if starts with /
466 Ch* query_; // Includes the ?
467 Ch* frag_; // Includes the #
468
471};
472
474typedef GenericUri<Value> Uri;
475
477
478#if defined(__clang__)
479RAPIDJSON_DIAG_POP
480#endif
481
482#endif // RAPIDJSON_URI_H_
GenericUri(const GenericUri &rhs, Allocator *allocator)
Copy constructor.
Definition uri.h:71
Allocator & GetAllocator()
Get the allocator of this GenericUri.
Definition uri.h:230
~GenericUri()
Destructor.
Definition uri.h:76
GenericUri(const GenericUri &rhs)
Copy constructor.
Definition uri.h:66
GenericUri Resolve(const GenericUri &baseuri, Allocator *allocator=0)
Resolve this URI against another (base) URI in accordance with URI resolution rules.
Definition uri.h:157
bool operator==(const GenericUri &rhs) const
Equality operators.
Definition uri.h:130
GenericUri & operator=(const GenericUri &rhs)
Assignment operator.
Definition uri.h:82
void Get(T &uri, Allocator &allocator)
Getters.
Definition uri.h:100
GenericUri(Allocator *allocator=0)
Constructors.
Definition uri.h:42
Concept for allocating, resizing and freeing memory block.
#define RAPIDJSON_ASSERT(x)
Assertion.
Definition rapidjson.h:438
#define RAPIDJSON_NAMESPACE_BEGIN
provide custom rapidjson namespace (opening expression)
Definition rapidjson.h:122
#define RAPIDJSON_NAMESPACE_END
provide custom rapidjson namespace (closing expression)
Definition rapidjson.h:125
Integer len(const char *s)
Retourne la longueur de la chaîne s.
#define RAPIDJSON_DELETE(x)
! customization point for global delete
Definition rapidjson.h:717
RAPIDJSON_NAMESPACE_BEGIN typedef unsigned SizeType
Size type (for string lengths, array sizes, etc.)
Definition rapidjson.h:416
#define RAPIDJSON_NEW(TypeName)
! customization point for global new
Definition rapidjson.h:713