Line data Source code
1 : //
2 : // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
3 : // Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
4 : //
5 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 : //
8 : // Official repository: https://github.com/boostorg/url
9 : //
10 :
11 :
12 : #include <boost/url/detail/config.hpp>
13 : #include <boost/url/decode_view.hpp>
14 : #include "decode.hpp"
15 : #include <boost/url/segments_encoded_view.hpp>
16 : #include <boost/url/grammar/ci_string.hpp>
17 : #include <boost/assert.hpp>
18 : #include <boost/core/ignore_unused.hpp>
19 : #include <cstring>
20 : #include "normalize.hpp"
21 :
22 : namespace boost {
23 : namespace urls {
24 : namespace detail {
25 :
26 : void
27 5604 : pop_encoded_front(
28 : core::string_view& s,
29 : char& c,
30 : std::size_t& n) noexcept
31 : {
32 5604 : if(s.front() != '%')
33 : {
34 5514 : c = s.front();
35 5514 : s.remove_prefix(1);
36 : }
37 : else
38 : {
39 90 : detail::decode_unsafe(
40 : &c,
41 : &c + 1,
42 : s.substr(0, 3));
43 90 : s.remove_prefix(3);
44 : }
45 5604 : ++n;
46 5604 : }
47 :
48 : int
49 77 : compare_encoded(
50 : core::string_view lhs,
51 : core::string_view rhs) noexcept
52 : {
53 77 : std::size_t n0 = 0;
54 77 : std::size_t n1 = 0;
55 77 : char c0 = 0;
56 77 : char c1 = 0;
57 205 : while(
58 535 : !lhs.empty() &&
59 253 : !rhs.empty())
60 : {
61 240 : pop_encoded_front(lhs, c0, n0);
62 240 : pop_encoded_front(rhs, c1, n1);
63 240 : if (c0 < c1)
64 20 : return -1;
65 220 : if (c1 < c0)
66 15 : return 1;
67 : }
68 42 : n0 += detail::decode_bytes_unsafe(lhs);
69 42 : n1 += detail::decode_bytes_unsafe(rhs);
70 42 : if (n0 == n1)
71 21 : return 0;
72 21 : if (n0 < n1)
73 8 : return -1;
74 13 : return 1;
75 : }
76 :
77 : void
78 1104 : digest_encoded(
79 : core::string_view s,
80 : fnv_1a& hasher) noexcept
81 : {
82 1104 : char c = 0;
83 1104 : std::size_t n = 0;
84 1556 : while(!s.empty())
85 : {
86 452 : pop_encoded_front(s, c, n);
87 452 : hasher.put(c);
88 : }
89 1104 : }
90 :
91 : int
92 119 : ci_compare_encoded(
93 : core::string_view lhs,
94 : core::string_view rhs) noexcept
95 : {
96 119 : std::size_t n0 = 0;
97 119 : std::size_t n1 = 0;
98 119 : char c0 = 0;
99 119 : char c1 = 0;
100 1505 : while (
101 3145 : !lhs.empty() &&
102 1521 : !rhs.empty())
103 : {
104 1515 : pop_encoded_front(lhs, c0, n0);
105 1515 : pop_encoded_front(rhs, c1, n1);
106 1515 : c0 = grammar::to_lower(c0);
107 1515 : c1 = grammar::to_lower(c1);
108 1515 : if (c0 < c1)
109 8 : return -1;
110 1507 : if (c1 < c0)
111 2 : return 1;
112 : }
113 109 : n0 += detail::decode_bytes_unsafe(lhs);
114 109 : n1 += detail::decode_bytes_unsafe(rhs);
115 109 : if (n0 == n1)
116 102 : return 0;
117 7 : if (n0 < n1)
118 1 : return -1;
119 6 : return 1;
120 : }
121 :
122 : void
123 276 : ci_digest_encoded(
124 : core::string_view s,
125 : fnv_1a& hasher) noexcept
126 : {
127 276 : char c = 0;
128 276 : std::size_t n = 0;
129 1918 : while(!s.empty())
130 : {
131 1642 : pop_encoded_front(s, c, n);
132 1642 : c = grammar::to_lower(c);
133 1642 : hasher.put(c);
134 : }
135 276 : }
136 :
137 : int
138 8 : compare(
139 : core::string_view lhs,
140 : core::string_view rhs) noexcept
141 : {
142 8 : auto rlen = (std::min)(lhs.size(), rhs.size());
143 18 : for (std::size_t i = 0; i < rlen; ++i)
144 : {
145 17 : char c0 = lhs[i];
146 17 : char c1 = rhs[i];
147 17 : if (c0 < c1)
148 6 : return -1;
149 11 : if (c1 < c0)
150 1 : return 1;
151 : }
152 1 : if ( lhs.size() == rhs.size() )
153 1 : return 0;
154 0 : if ( lhs.size() < rhs.size() )
155 0 : return -1;
156 0 : return 1;
157 : }
158 :
159 : int
160 156 : ci_compare(
161 : core::string_view lhs,
162 : core::string_view rhs) noexcept
163 : {
164 156 : auto rlen = (std::min)(lhs.size(), rhs.size());
165 789 : for (std::size_t i = 0; i < rlen; ++i)
166 : {
167 640 : char c0 = grammar::to_lower(lhs[i]);
168 640 : char c1 = grammar::to_lower(rhs[i]);
169 640 : if (c0 < c1)
170 6 : return -1;
171 634 : if (c1 < c0)
172 1 : return 1;
173 : }
174 149 : if ( lhs.size() == rhs.size() )
175 142 : return 0;
176 7 : if ( lhs.size() < rhs.size() )
177 6 : return -1;
178 1 : return 1;
179 : }
180 :
181 : void
182 276 : ci_digest(
183 : core::string_view s,
184 : fnv_1a& hasher) noexcept
185 : {
186 866 : for (char c: s)
187 : {
188 590 : c = grammar::to_lower(c);
189 590 : hasher.put(c);
190 : }
191 276 : }
192 :
193 : std::size_t
194 0 : path_starts_with(
195 : core::string_view lhs,
196 : core::string_view rhs) noexcept
197 : {
198 0 : auto consume_one = [](
199 : core::string_view::iterator& it,
200 : char &c)
201 : {
202 0 : if(*it != '%')
203 : {
204 0 : c = *it;
205 0 : ++it;
206 0 : return;
207 : }
208 0 : detail::decode_unsafe(
209 : &c,
210 : &c + 1,
211 : core::string_view(it, 3));
212 0 : if (c != '/')
213 : {
214 0 : it += 3;
215 0 : return;
216 : }
217 0 : c = *it;
218 0 : ++it;
219 : };
220 :
221 0 : auto it0 = lhs.begin();
222 0 : auto it1 = rhs.begin();
223 0 : auto end0 = lhs.end();
224 0 : auto end1 = rhs.end();
225 0 : char c0 = 0;
226 0 : char c1 = 0;
227 0 : while (
228 0 : it0 < end0 &&
229 0 : it1 < end1)
230 : {
231 0 : consume_one(it0, c0);
232 0 : consume_one(it1, c1);
233 0 : if (c0 != c1)
234 0 : return 0;
235 : }
236 0 : if (it1 == end1)
237 0 : return it0 - lhs.begin();
238 0 : return 0;
239 : }
240 :
241 : std::size_t
242 2104 : path_ends_with(
243 : core::string_view lhs,
244 : core::string_view rhs) noexcept
245 : {
246 5800 : auto consume_last = [](
247 : core::string_view::iterator& it,
248 : core::string_view::iterator& end,
249 : char& c)
250 : {
251 9732 : if ((end - it) < 3 ||
252 3932 : *(std::prev(end, 3)) != '%')
253 : {
254 5768 : c = *--end;
255 5768 : return;
256 : }
257 32 : detail::decode_unsafe(
258 : &c,
259 : &c + 1,
260 : core::string_view(std::prev(
261 : end, 3), 3));
262 32 : if (c != '/')
263 : {
264 32 : end -= 3;
265 32 : return;
266 : }
267 0 : c = *--end;
268 : };
269 :
270 2104 : auto it0 = lhs.begin();
271 2104 : auto it1 = rhs.begin();
272 2104 : auto end0 = lhs.end();
273 2104 : auto end1 = rhs.end();
274 2104 : char c0 = 0;
275 2104 : char c1 = 0;
276 1104 : while(
277 3208 : it0 < end0 &&
278 2974 : it1 < end1)
279 : {
280 2900 : consume_last(it0, end0, c0);
281 2900 : consume_last(it1, end1, c1);
282 2900 : if (c0 != c1)
283 1796 : return 0;
284 : }
285 308 : if (it1 == end1)
286 110 : return lhs.end() - end0;
287 198 : return 0;
288 : }
289 :
290 : std::size_t
291 831 : remove_dot_segments(
292 : char* dest0,
293 : char const* end,
294 : core::string_view input) noexcept
295 : {
296 : // 1. The input buffer `s` is initialized with
297 : // the now-appended path components and the
298 : // output buffer `dest0` is initialized to
299 : // the empty string.
300 831 : char* dest = dest0;
301 831 : bool const is_absolute = input.starts_with('/');
302 :
303 : // Step 2 is a loop through 5 production rules:
304 : // https://www.rfc-editor.org/rfc/rfc3986#section-5.2.4
305 : //
306 : // There are no transitions between all rules,
307 : // which enables some optimizations.
308 : //
309 : // Initial:
310 : // - Rule A: handle initial dots
311 : // If the input buffer begins with a
312 : // prefix of "../" or "./", then remove
313 : // that prefix from the input buffer.
314 : // Rule A can only happen at the beginning.
315 : // Errata 4547: Keep "../" in the beginning
316 : // https://www.rfc-editor.org/errata/eid4547
317 : //
318 : // Then:
319 : // - Rule D: ignore a final ".." or "."
320 : // if the input buffer consists only of "."
321 : // or "..", then remove that from the input
322 : // buffer.
323 : // Rule D can only happen after Rule A because:
324 : // - B and C write "/" to the input
325 : // - E writes "/" to input or returns
326 : //
327 : // Then:
328 : // - Rule B: ignore ".": write "/" to the input
329 : // - Rule C: apply "..": remove seg and write "/"
330 : // - Rule E: copy complete segment
331 : auto append =
332 1491 : [](char*& first, char const* last, core::string_view in)
333 : {
334 : // append `in` to `dest`
335 1491 : BOOST_ASSERT(in.size() <= std::size_t(last - first));
336 1491 : std::memmove(first, in.data(), in.size());
337 1491 : first += in.size();
338 : ignore_unused(last);
339 1491 : };
340 :
341 9555 : auto dot_starts_with = [](
342 : core::string_view str, core::string_view dots, std::size_t& n)
343 : {
344 : // starts_with for encoded/decoded dots
345 : // or decoded otherwise. return how many
346 : // chars in str match the dots
347 9555 : n = 0;
348 16906 : for (char c: dots)
349 : {
350 16356 : if (str.starts_with(c))
351 : {
352 7351 : str.remove_prefix(1);
353 7351 : ++n;
354 : }
355 9005 : else if (str.size() > 2 &&
356 6243 : str[0] == '%' &&
357 15264 : str[1] == '2' &&
358 16 : (str[2] == 'e' ||
359 16 : str[2] == 'E'))
360 : {
361 0 : str.remove_prefix(3);
362 0 : n += 3;
363 : }
364 : else
365 : {
366 9005 : n = 0;
367 9005 : return false;
368 : }
369 : }
370 550 : return true;
371 : };
372 :
373 4773 : auto dot_equal = [&dot_starts_with](
374 4773 : core::string_view str, core::string_view dots)
375 : {
376 4773 : std::size_t n = 0;
377 4773 : dot_starts_with(str, dots, n);
378 4773 : return n == str.size();
379 831 : };
380 :
381 : // Rule A
382 : std::size_t n;
383 847 : while (!input.empty())
384 : {
385 766 : if (dot_starts_with(input, "../", n))
386 : {
387 : // Errata 4547
388 4 : append(dest, end, "../");
389 4 : input.remove_prefix(n);
390 4 : continue;
391 : }
392 762 : else if (!dot_starts_with(input, "./", n))
393 : {
394 750 : break;
395 : }
396 12 : input.remove_prefix(n);
397 : }
398 :
399 : // Rule D
400 831 : if( dot_equal(input, "."))
401 : {
402 82 : input = {};
403 : }
404 749 : else if( dot_equal(input, "..") )
405 : {
406 : // Errata 4547
407 3 : append(dest, end, "..");
408 3 : input = {};
409 : }
410 :
411 : // 2. While the input buffer is not empty,
412 : // loop as follows:
413 2439 : while (!input.empty())
414 : {
415 : // Rule B
416 1647 : bool const is_dot_seg = dot_starts_with(input, "/./", n);
417 1647 : if (is_dot_seg)
418 : {
419 32 : input.remove_prefix(n - 1);
420 32 : continue;
421 : }
422 :
423 1615 : bool const is_final_dot_seg = dot_equal(input, "/.");
424 1615 : if (is_final_dot_seg)
425 : {
426 : // We can't remove "." from a core::string_view
427 : // So what we do here is equivalent to
428 : // replacing s with '/' as required
429 : // in Rule B and executing the next
430 : // iteration, which would append this
431 : // '/' to the output, as required by
432 : // Rule E
433 8 : append(dest, end, input.substr(0, 1));
434 8 : input = {};
435 8 : break;
436 : }
437 :
438 : // Rule C
439 1607 : bool const is_dotdot_seg = dot_starts_with(input, "/../", n);
440 1607 : if (is_dotdot_seg)
441 : {
442 193 : core::string_view cur_out(dest0, dest - dest0);
443 193 : std::size_t p = cur_out.find_last_of('/');
444 193 : bool const has_multiple_segs = p != core::string_view::npos;
445 193 : if (has_multiple_segs)
446 : {
447 : // output has multiple segments
448 : // "erase" [p, end] if not "/.."
449 132 : core::string_view last_seg(dest0 + p, dest - (dest0 + p));
450 132 : bool const prev_is_dotdot_seg = dot_equal(last_seg, "/..");
451 132 : if (!prev_is_dotdot_seg)
452 : {
453 121 : dest = dest0 + p;
454 : }
455 : else
456 : {
457 11 : append(dest, end, "/..");
458 : }
459 : }
460 61 : else if (dest0 != dest)
461 : {
462 : // Only one segment in the output: remove it
463 11 : core::string_view last_seg(dest0, dest - dest0);
464 11 : bool const prev_is_dotdot_seg = dot_equal(last_seg, "..");
465 11 : if (!prev_is_dotdot_seg)
466 : {
467 9 : dest = dest0;
468 9 : if (!is_absolute)
469 : {
470 9 : input.remove_prefix(1);
471 : }
472 : }
473 : else
474 : {
475 2 : append(dest, end, "/..");
476 : }
477 : }
478 : else
479 : {
480 : // Output is empty
481 50 : if (is_absolute)
482 : {
483 50 : append(dest, end, "/..");
484 : }
485 : else
486 : {
487 0 : append(dest, end, "..");
488 : }
489 : }
490 193 : input.remove_prefix(n - 1);
491 193 : continue;
492 : }
493 :
494 1414 : bool const is_final_dotdot_seg = dot_equal(input, "/..");
495 1414 : if (is_final_dotdot_seg)
496 : {
497 31 : core::string_view cur_out(dest0, dest - dest0);
498 31 : std::size_t p = cur_out.find_last_of('/');
499 31 : bool const has_multiple_segs = p != core::string_view::npos;
500 31 : if (has_multiple_segs)
501 : {
502 : // output has multiple segments
503 : // "erase" [p, end] if not "/.."
504 18 : core::string_view last_seg(dest0 + p, dest - (dest0 + p));
505 18 : bool const prev_is_dotdot_seg = dot_equal(last_seg, "/..");
506 18 : if (!prev_is_dotdot_seg)
507 : {
508 14 : dest = dest0 + p;
509 14 : append(dest, end, "/");
510 : }
511 : else
512 : {
513 4 : append(dest, end, "/..");
514 : }
515 : }
516 13 : else if (dest0 != dest)
517 : {
518 : // Only one segment in the output: remove it
519 3 : core::string_view last_seg(dest0, dest - dest0);
520 3 : bool const prev_is_dotdot_seg = dot_equal(last_seg, "..");
521 3 : if (!prev_is_dotdot_seg) {
522 1 : dest = dest0;
523 : }
524 : else
525 : {
526 2 : append(dest, end, "/..");
527 : }
528 : }
529 : else
530 : {
531 : // Output is empty: append dotdot
532 10 : if (is_absolute)
533 : {
534 10 : append(dest, end, "/..");
535 : }
536 : else
537 : {
538 0 : append(dest, end, "..");
539 : }
540 : }
541 31 : input = {};
542 31 : break;
543 : }
544 :
545 : // Rule E
546 1383 : std::size_t p = input.find_first_of('/', 1);
547 1383 : if (p != core::string_view::npos)
548 : {
549 676 : append(dest, end, input.substr(0, p));
550 676 : input.remove_prefix(p);
551 : }
552 : else
553 : {
554 707 : append(dest, end, input);
555 707 : input = {};
556 : }
557 : }
558 :
559 : // 3. Finally, the output buffer is set
560 : // as the result of remove_dot_segments,
561 : // and we return its size
562 831 : return dest - dest0;
563 : }
564 :
565 : char
566 1138 : path_pop_back( core::string_view& s )
567 : {
568 1656 : if (s.size() < 3 ||
569 518 : *std::prev(s.end(), 3) != '%')
570 : {
571 1090 : char c = s.back();
572 1090 : s.remove_suffix(1);
573 1090 : return c;
574 : }
575 48 : char c = 0;
576 96 : detail::decode_unsafe(
577 96 : &c, &c + 1, s.substr(s.size() - 3));
578 48 : if (c != '/')
579 : {
580 44 : s.remove_suffix(3);
581 44 : return c;
582 : }
583 4 : c = s.back();
584 4 : s.remove_suffix(1);
585 4 : return c;
586 : };
587 :
588 : void
589 506 : pop_last_segment(
590 : core::string_view& s,
591 : core::string_view& c,
592 : std::size_t& level,
593 : bool r) noexcept
594 : {
595 506 : c = {};
596 506 : std::size_t n = 0;
597 668 : while (!s.empty())
598 : {
599 : // B. if the input buffer begins with a
600 : // prefix of "/./" or "/.", where "." is
601 : // a complete path segment, then replace
602 : // that prefix with "/" in the input
603 : // buffer; otherwise,
604 550 : n = detail::path_ends_with(s, "/./");
605 550 : if (n)
606 : {
607 10 : c = s.substr(s.size() - n);
608 10 : s.remove_suffix(n);
609 10 : continue;
610 : }
611 540 : n = detail::path_ends_with(s, "/.");
612 540 : if (n)
613 : {
614 12 : c = s.substr(s.size() - n, 1);
615 12 : s.remove_suffix(n);
616 12 : continue;
617 : }
618 :
619 : // C. if the input buffer begins with a
620 : // prefix of "/../" or "/..", where ".."
621 : // is a complete path segment, then
622 : // replace that prefix with "/" in the
623 : // input buffer and remove the last
624 : // segment and its preceding "/"
625 : // (if any) from the output buffer
626 : // otherwise,
627 528 : n = detail::path_ends_with(s, "/../");
628 528 : if (n)
629 : {
630 42 : c = s.substr(s.size() - n);
631 42 : s.remove_suffix(n);
632 42 : ++level;
633 42 : continue;
634 : }
635 486 : n = detail::path_ends_with(s, "/..");
636 486 : if (n)
637 : {
638 46 : c = s.substr(s.size() - n);
639 46 : s.remove_suffix(n);
640 46 : ++level;
641 46 : continue;
642 : }
643 :
644 : // E. move the first path segment in the
645 : // input buffer to the end of the output
646 : // buffer, including the initial "/"
647 : // character (if any) and any subsequent
648 : // characters up to, but not including,
649 : // the next "/" character or the end of
650 : // the input buffer.
651 440 : std::size_t p = s.size() > 1
652 440 : ? s.find_last_of('/', s.size() - 2)
653 440 : : core::string_view::npos;
654 440 : if (p != core::string_view::npos)
655 : {
656 272 : c = s.substr(p + 1);
657 272 : s.remove_suffix(c.size());
658 : }
659 : else
660 : {
661 168 : c = s;
662 168 : s = {};
663 : }
664 :
665 440 : if (level == 0)
666 388 : return;
667 52 : if (!s.empty())
668 42 : --level;
669 : }
670 : // we still need to skip n_skip + 1
671 : // but the string is empty
672 118 : if (r && level)
673 : {
674 34 : c = "/";
675 34 : level = 0;
676 34 : return;
677 : }
678 84 : else if (level)
679 : {
680 4 : if (c.empty())
681 0 : c = "/..";
682 : else
683 4 : c = "/../";
684 4 : --level;
685 4 : return;
686 : }
687 80 : c = {};
688 : }
689 :
690 : void
691 276 : normalized_path_digest(
692 : core::string_view s,
693 : bool remove_unmatched,
694 : fnv_1a& hasher) noexcept
695 : {
696 276 : core::string_view child;
697 276 : std::size_t level = 0;
698 230 : do
699 : {
700 506 : pop_last_segment(
701 : s, child, level, remove_unmatched);
702 1644 : while (!child.empty())
703 : {
704 1138 : char c = path_pop_back(child);
705 1138 : hasher.put(c);
706 : }
707 : }
708 506 : while (!s.empty());
709 276 : }
710 :
711 : // compare segments as if there were a normalized
712 : int
713 168 : segments_compare(
714 : segments_encoded_view seg0,
715 : segments_encoded_view seg1) noexcept
716 : {
717 : // calculate path size as if it were normalized
718 : auto normalized_size =
719 336 : [](segments_encoded_view seg) -> std::size_t
720 : {
721 336 : if (seg.empty())
722 102 : return seg.is_absolute();
723 :
724 234 : std::size_t n = 0;
725 234 : std::size_t skip = 0;
726 234 : auto begin = seg.begin();
727 234 : auto it = seg.end();
728 892 : while (it != begin)
729 : {
730 658 : --it;
731 658 : decode_view dseg = **it;
732 658 : if (dseg == "..")
733 167 : ++skip;
734 491 : else if (dseg != ".")
735 : {
736 453 : if (skip)
737 85 : --skip;
738 : else
739 368 : n += dseg.size() + 1;
740 : }
741 : }
742 234 : n += skip * 3;
743 234 : n -= !seg.is_absolute();
744 234 : return n;
745 : };
746 :
747 : // find the normalized size for the comparison
748 168 : std::size_t n0 = normalized_size(seg0);
749 168 : std::size_t n1 = normalized_size(seg1);
750 168 : std::size_t n00 = n0;
751 168 : std::size_t n10 = n1;
752 :
753 : // consume the last char from a segment range
754 : auto consume_last =
755 1632 : [](
756 : std::size_t& n,
757 : decode_view& dseg,
758 : segments_encoded_view::iterator& begin,
759 : segments_encoded_view::iterator& it,
760 : decode_view::iterator& cit,
761 : std::size_t& skip,
762 : bool& at_slash) -> char
763 : {
764 1632 : if (cit != dseg.begin())
765 : {
766 : // return last char from current segment
767 1005 : at_slash = false;
768 1005 : --cit;
769 1005 : --n;
770 1005 : return *cit;
771 : }
772 :
773 627 : if (!at_slash)
774 : {
775 : // current segment dseg is over and
776 : // previous char was not a slash
777 : // so we output one
778 367 : at_slash = true;
779 367 : --n;
780 367 : return '/';
781 : }
782 :
783 : // current segment dseg is over and
784 : // last char was already the slash
785 : // between segments, so take the
786 : // next final segment to consume
787 260 : at_slash = false;
788 498 : while (cit == dseg.begin())
789 : {
790 : // take next segment
791 498 : if (it != begin)
792 376 : --it;
793 : else
794 122 : break;
795 376 : if (**it == "..")
796 : {
797 : // skip next if this is ".."
798 140 : ++skip;
799 : }
800 236 : else if (**it != ".")
801 : {
802 208 : if (skip)
803 : {
804 : // discount skips
805 70 : --skip;
806 : }
807 : else
808 : {
809 : // or update current seg
810 138 : dseg = **it;
811 138 : cit = dseg.end();
812 138 : break;
813 : }
814 : }
815 : }
816 : // consume from the new current
817 : // segment
818 260 : --n;
819 260 : if (cit != dseg.begin())
820 : {
821 : // in the general case, we consume
822 : // one more character from the end
823 123 : --cit;
824 123 : return *cit;
825 : }
826 :
827 : // nothing left to consume in the
828 : // current and new segment
829 137 : if (it == begin)
830 : {
831 : // if this is the first
832 : // segment, the segments are
833 : // over and there can only
834 : // be repetitions of "../" to
835 : // output
836 128 : return "/.."[n % 3];
837 : }
838 : // at other segments, we need
839 : // a slash to transition to the
840 : // next segment
841 9 : at_slash = true;
842 9 : return '/';
843 : };
844 :
845 : // consume final segments from seg0 that
846 : // should not influence the comparison
847 168 : auto begin0 = seg0.begin();
848 168 : auto it0 = seg0.end();
849 168 : decode_view dseg0;
850 168 : if (it0 != seg0.begin())
851 : {
852 117 : --it0;
853 117 : dseg0 = **it0;
854 : }
855 168 : decode_view::iterator cit0 = dseg0.end();
856 168 : std::size_t skip0 = 0;
857 168 : bool at_slash0 = true;
858 302 : while (n0 > n1)
859 : {
860 134 : consume_last(n0, dseg0, begin0, it0, cit0, skip0, at_slash0);
861 : }
862 :
863 : // consume final segments from seg1 that
864 : // should not influence the comparison
865 168 : auto begin1 = seg1.begin();
866 168 : auto it1 = seg1.end();
867 168 : decode_view dseg1;
868 168 : if (it1 != seg1.begin())
869 : {
870 117 : --it1;
871 117 : dseg1 = **it1;
872 : }
873 168 : decode_view::iterator cit1 = dseg1.end();
874 168 : std::size_t skip1 = 0;
875 168 : bool at_slash1 = true;
876 202 : while (n1 > n0)
877 : {
878 34 : consume_last(n1, dseg1, begin1, it1, cit1, skip1, at_slash1);
879 : }
880 :
881 168 : int cmp = 0;
882 900 : while (n0)
883 : {
884 732 : char c0 = consume_last(
885 : n0, dseg0, begin0, it0, cit0, skip0, at_slash0);
886 732 : char c1 = consume_last(
887 : n1, dseg1, begin1, it1, cit1, skip1, at_slash1);
888 732 : if (c0 < c1)
889 36 : cmp = -1;
890 696 : else if (c1 < c0)
891 41 : cmp = +1;
892 : }
893 :
894 168 : if (cmp != 0)
895 41 : return cmp;
896 127 : if ( n00 == n10 )
897 125 : return 0;
898 2 : if ( n00 < n10 )
899 1 : return -1;
900 1 : return 1;
901 : }
902 :
903 : } // detail
904 : } // urls
905 : } // boost
906 :
907 :
|