Open C++ Libraries.org develop
Loading...
Searching...
No Matches
rope_fwd.inl
1// SPDX-License-Identifier: BSL-1.0
2// Copyright 2025-2026, Amlal El Mahrouss (amlal@nekernel.org)
3// Distributed under the Boost Software License, Version 1.0. (See accompanying
4// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5// Official repository: https://github.com/open-cpp-libraries/tproc
6
7#ifndef OCL_TPROC_ROPE_FWD_INL
8#define OCL_TPROC_ROPE_FWD_INL
9
10#include <boost/system/error_code.hpp>
11
12namespace ocl::tproc
13{
14
16 template <class CharT, class Traits, class Allocator>
17 struct basic_rope<CharT, Traits, Allocator>::tree_impl
18 {
19 using char_type = CharT;
20 using value_type = char_type;
21 using size_type = typename std::allocator_traits<Allocator>::size_type;
22 using error_type = boost::system::error_code;
23 using allocator_type = Allocator;
25
26 // B-Tree fields.
27 rope_ptr left_{nullptr}; // Left child (internal node only)
28 rope_ptr right_{nullptr}; // Right child (internal node only)
29
30 size_type weight_{0}; // Size of left subtree (internal) OR to_string size (leaf)
31 value_type* blob_{nullptr}; // Character to_string (leaf node only)
32 allocator_type alloc_;
33 size_type capacity_{0}; // Allocated blob capacity
34
35 public:
36 tree_impl(Allocator alloc = Allocator())
37 : alloc_(alloc)
38 {
39 }
40
41 bool is_leaf() const
42 {
43 return blob_ != nullptr;
44 }
45
46 tree_impl(const boost::core::basic_string_view<CharT>& str,
47 Allocator alloc = Allocator())
48 : weight_(str.size()), alloc_(alloc), capacity_(str.size())
49 {
50 if (weight_ > 0)
51 {
52 blob_ = alloc_.allocate(capacity_);
53
54 if (!blob_)
55 detail::throw_bad_alloc();
56
57 ::memset(blob_, 0, capacity_);
58 Traits::copy(blob_, str.data(), weight_);
59 }
60 }
61
62 tree_impl(rope_ptr left,
63 rope_ptr right,
64 Allocator alloc = Allocator())
65 : left_(left), right_(right), alloc_(alloc)
66 {
67 weight_ = left ? left->impl_->total_size() : 0;
68 }
69
71 {
72 if (blob_ && capacity_ > 0)
73 {
74 alloc_.deallocate(blob_, capacity_);
75 }
76 }
77
78 size_type total_size() const
79 {
80 if (is_leaf())
81 {
82 return weight_;
83 }
84
85 return weight_ + (right_ ? right_->impl_->total_size() : 0);
86 }
87
88 size_type size() const
89 {
90 return total_size();
91 }
92
93 CharT at(size_type pos) const
94 {
95 if (is_leaf())
96 {
97 if (pos >= weight_)
98 {
99 throw std::out_of_range("rope index out of range");
100 }
101
102 return blob_[pos];
103 }
104
105 // Internal node: navigate based on weight
106 if (pos < weight_)
107 {
108 if (!left_)
109 throw std::out_of_range("null left child");
110 return left_->impl_->at(pos);
111 }
112 else
113 {
114 if (!right_)
115 throw std::out_of_range("null right child");
116 return right_->impl_->at(pos - weight_);
117 }
118 }
119
120 public:
121 rope_ptr concat(rope_ptr left,
122 rope_ptr right)
123 {
124 if (!left)
125 return right;
126
127 if (!right)
128 return left;
129
130 right->impl_->left_ = left;
131 left->impl_->right_ = right;
132
133 return left;
134 }
135
136 std::pair<rope_ptr, rope_ptr> split(size_type pos, rope_ptr this_rope, Allocator alloc = Allocator())
137 {
138 if (is_leaf())
139 {
140 // Split leaf node
141 if (pos == 0)
142 return {nullptr, this_rope};
143
144 if (pos >= weight_)
145 return {this_rope, nullptr};
146
147 auto left_view = boost::core::basic_string_view<CharT>(blob_, pos);
148 auto right_view = boost::core::basic_string_view<CharT>(blob_ + pos, weight_ - pos);
149
150 auto* left_rope = new basic_rope<CharT, Traits, Allocator>(left_view);
151
152 if (!left_rope)
153 detail::throw_bad_alloc();
154
155 auto* right_rope = new basic_rope<CharT, Traits, Allocator>(right_view);
156
157 if (!right_rope)
158 {
159 delete left_rope;
160 left_rope = nullptr;
161
162 detail::throw_bad_alloc();
163 }
164
165 return {left_rope, right_rope};
166 }
167
168 // Internal node
169 if (pos < weight_)
170 {
171 auto [ll, lr] = left_->impl_->split(pos, left_, alloc);
172 return {ll, concat(lr, right_, alloc)};
173 }
174 else if (pos > weight_)
175 {
176 auto [rl, rr] = right_->impl_->split(pos - weight_, right_, alloc);
177 return {concat(left_, rl, alloc), rr};
178 }
179 else
180 {
181 return {left_, right_};
182 }
183 }
184
185 rope_ptr insert(size_type pos, const boost::core::basic_string_view<CharT>& str, rope_ptr this_rope, Allocator alloc = Allocator())
186 {
187 auto [left, right] = split(pos, this_rope, alloc);
188 auto* middle = new basic_rope<CharT, Traits, Allocator>(str);
189 return concat(concat(left, middle, alloc), right, alloc);
190 }
191
192 rope_ptr substr(size_type pos, size_type len, rope_ptr this_rope, Allocator alloc = Allocator())
193 {
194 auto [_, right] = split(pos, this_rope, alloc);
195
196 if (!right)
197 return nullptr;
198
199 if (len == 0UL || len == basic_rope<CharT, Traits, Allocator>::npos)
200 return right;
201
202 auto [result, __] = right->impl_->split(len, right, alloc);
203
204 return result;
205 }
206
207 bool starts_with(const boost::core::basic_string_view<CharT>& prefix) const
208 {
209 if (prefix.size() > total_size())
210 return false;
211
212 size_type checked{};
213 return check_prefix(prefix, checked);
214 }
215
216 bool ends_with(const boost::core::basic_string_view<CharT>& suffix) const
217 {
218 size_type total = total_size();
219
220 if (suffix.size() > total)
221 return false;
222
223 size_type start_pos = total - suffix.size();
224 return check_suffix(suffix, start_pos, 0);
225 }
226
227 bool equals(const tree_impl* other) const
228 {
229 if (!other)
230 return false;
231 if (total_size() != other->total_size())
232 return false;
233
234 // Compare character by character (could be optimized)
235 size_type sz = total_size();
236 for (size_type i = 0; i < sz; ++i)
237 {
238 if (at(i) != other->at(i))
239 return false;
240 }
241 return true;
242 }
243
244 bool equals(const boost::core::basic_string_view<CharT>& str) const
245 {
246 if (total_size() != str.size())
247 return false;
248
249 for (size_type i = 0; i < str.size(); ++i)
250 {
251 if (at(i) != str[i])
252 return false;
253 }
254 return true;
255 }
256
257 private:
258 bool check_prefix(const boost::core::basic_string_view<CharT>& prefix, size_type& checked) const
259 {
260 if (is_leaf())
261 {
262 size_type to_check = std::min(weight_, prefix.size() - checked);
263
264 if (Traits::compare(blob_, prefix.c_str() + checked, to_check) != 0)
265 return false;
266 checked += to_check;
267
268 return checked >= prefix.size();
269 }
270
271 // Internal node
272 if (left_ && !left_->impl_->check_prefix(prefix, checked))
273 return false;
274 if (checked >= prefix.size())
275 return true;
276 if (right_)
277 return right_->impl_->check_prefix(prefix, checked);
278 return checked >= prefix.size();
279 }
280
281 bool check_suffix(const boost::core::basic_string_view<CharT>& suffix, size_type rope_pos, size_type suffix_pos) const
282 {
283 if (is_leaf())
284 {
285 if (rope_pos >= weight_)
286 return suffix_pos >= suffix.size();
287
288 size_type to_check = std::min(weight_ - rope_pos, suffix.size() - suffix_pos);
289
290 if (Traits::compare(blob_ + rope_pos, suffix.to_string() + suffix_pos, to_check) != 0)
291 return false;
292
293 return suffix_pos + to_check >= suffix.size();
294 }
295
296 // Internal node
297 if (rope_pos < weight_)
298 {
299 if (!left_)
300 return false;
301
302 size_type left_check = std::min(weight_ - rope_pos, suffix.size() - suffix_pos);
303
304 if (!left_->impl_->check_suffix(suffix, rope_pos, suffix_pos))
305 return false;
306
307 suffix_pos += left_check;
308
309 if (suffix_pos >= suffix.size())
310 return true;
311
312 rope_pos = 0;
313 }
314 else
315 {
316 rope_pos -= weight_;
317 }
318
319 if (right_)
320 return right_->impl_->check_suffix(suffix, rope_pos, suffix_pos);
321
322 return suffix_pos >= suffix.size();
323 }
324 };
325
326 template <class CharT, class Traits, class Allocator>
328 {
329 delete impl_;
330 impl_ = nullptr;
331 }
332
333 template <class CharT, class Traits, class Allocator>
337 {
338 impl_ = std::exchange(other.impl_, nullptr);
339 return *this;
340 }
341
342 template <class CharT, class Traits, class Allocator>
343 basic_rope<CharT, Traits, Allocator>::basic_rope(
344 basic_rope<CharT, Traits, Allocator>&& other)
345 {
346 impl_ = std::exchange(other.impl_, nullptr);
347 }
348
349 template <class CharT, class Traits, class Allocator>
350 basic_rope<CharT, Traits, Allocator>::basic_rope(
351 const boost::core::basic_string_view<CharT>& in)
352 : impl_(new tree_impl(in, Allocator()))
353 {
354 }
355
356 template <class CharT, class Traits, class Allocator>
357 basic_rope<CharT, Traits, Allocator>::rope_ptr basic_rope<CharT, Traits, Allocator>::begin()
358 {
359 if (impl_->is_leaf())
360 return this;
361
362 return impl_->left_ ? impl_->left_ : impl_->right_;
363 }
364
365 template <class CharT, class Traits, class Allocator>
366 basic_rope<CharT, Traits, Allocator>::rope_ptr basic_rope<CharT, Traits, Allocator>::end()
367 {
368 return nullptr;
369 }
370
372 template <class CharT, class Traits, class Allocator>
373 basic_rope<CharT, Traits, Allocator>::rope_ptr basic_rope<CharT, Traits, Allocator>::rbegin()
374 {
375 if (impl_->is_leaf())
376 return this;
377
378 auto last = impl_->right_;
379 auto ret = nullptr;
380
381 while (last)
382 {
383 ret = last;
384 last = last->right_;
385 }
386
387 return ret;
388 }
389
390 template <class CharT, class Traits, class Allocator>
391 basic_rope<CharT, Traits, Allocator>::rope_ptr basic_rope<CharT, Traits, Allocator>::rend()
392 {
393 return nullptr;
394 }
395
396 template <class CharT, class Traits, class Allocator>
397 const basic_rope<CharT, Traits, Allocator>::rope_ptr basic_rope<CharT, Traits, Allocator>::cbegin() const
398 {
399 if (impl_->is_leaf())
400 return this;
401
402 return impl_->left_ ? impl_->left_ : impl_->right_;
403 }
404
405 template <class CharT, class Traits, class Allocator>
406 const basic_rope<CharT, Traits, Allocator>::rope_ptr basic_rope<CharT, Traits, Allocator>::cend() const
407 {
408 return nullptr;
409 }
410
411 template <class CharT, class Traits, class Allocator>
412 basic_rope<CharT, Traits, Allocator>::size_type
413 basic_rope<CharT, Traits, Allocator>::size() const
414 {
415 return impl_->size();
416 }
417
418 template <class CharT, class Traits, class Allocator>
419 bool basic_rope<CharT, Traits, Allocator>::empty() const
420 {
421 return impl_->size() < 1UL;
422 }
423
424 template <class CharT, class Traits, class Allocator>
425 basic_rope<CharT, Traits, Allocator>&
426 basic_rope<CharT, Traits, Allocator>::substr(size_type pos, const size_type n)
427 {
428 if (!impl_)
429 detail::throw_bad_alloc();
430
431 auto* result = impl_->substr(pos, n, this, Allocator());
432
433 if (!result)
434 detail::throw_bad_alloc();
435
436 return *result;
437 }
438
439 template <class CharT, class Traits, class Allocator>
440 basic_rope<CharT, Traits, Allocator>::size_type
441 basic_rope<CharT, Traits, Allocator>::at(const boost::core::basic_string_view<CharT>& needle)
442 {
443 if (!impl_ || needle.empty())
444 return npos;
445
446 size_type rope_size = impl_->size();
447
448 if (needle.size() > rope_size)
449 return npos;
450
451 for (size_type i{}; i <= (rope_size - needle.size()); ++i)
452 {
453 bool match{true};
454
455 for (size_type j{}; j < needle.size(); ++j)
456 {
457 if (impl_->at(i + j) != needle[j])
458 {
459 match = false;
460 break;
461 }
462 }
463
464 if (match)
465 return i;
466 }
467
468 return npos;
469 }
470
471 template <class CharT, class Traits, class Allocator>
472 bool basic_rope<CharT, Traits, Allocator>::starts_with(const basic_rope<CharT>& other)
473 {
474 if (!impl_ || !other.impl_)
475 return false;
476
477 size_type other_size = other.impl_->size();
478
479 if (other_size > impl_->size())
480 return false;
481
482 for (size_type i = 0; i < other_size; ++i)
483 {
484 if (impl_->at(i) != other.impl_->at(i))
485 return false;
486 }
487
488 return true;
489 }
490
491 template <class CharT, class Traits, class Allocator>
492 bool basic_rope<CharT, Traits, Allocator>::ends_with(const basic_rope<CharT>& other)
493 {
494 if (!impl_ || !other.impl_)
495 return false;
496
497 size_type this_size = impl_->size();
498 size_type other_size = other.impl_->size();
499
500 if (other_size > this_size)
501 return false;
502
503 size_type offset = this_size - other_size;
504
505 for (size_type i = 0; i < other_size; ++i)
506 {
507 if (impl_->at(offset + i) != other.impl_->at(i))
508 return false;
509 }
510
511 return true;
512 }
513
514 template <class CharT, class Traits, class Allocator>
515 bool basic_rope<CharT, Traits, Allocator>::starts_with(const boost::core::basic_string_view<CharT>& prefix)
516 {
517 return impl_->starts_with(prefix);
518 }
519
520 template <class CharT, class Traits, class Allocator>
521 bool basic_rope<CharT, Traits, Allocator>::ends_with(const boost::core::basic_string_view<CharT>& suffix)
522 {
523 return impl_->ends_with(suffix);
524 }
525
526 template <class CharT, class Traits, class Allocator>
527 bool basic_rope<CharT, Traits, Allocator>::operator==(const basic_rope& other)
528 {
529 if (!impl_ && !other.impl_)
530 return true;
531
532 if (!impl_ || !other.impl_)
533 return false;
534
535 return impl_->equals(other.impl_);
536 }
537
538 template <class CharT, class Traits, class Allocator>
539 bool basic_rope<CharT, Traits, Allocator>::operator!=(const basic_rope& other)
540 {
541 if (!impl_)
542 return false;
543
544 return !(*this == other);
545 }
546
547 template <class CharT, class Traits, class Allocator>
548 bool basic_rope<CharT, Traits, Allocator>::operator==(const boost::core::basic_string_view<CharT>& str)
549 {
550 if (!impl_)
551 return false;
552
553 return impl_->equals(str);
554 }
555
556 template <class CharT, class Traits, class Allocator>
557 std::basic_string<typename basic_rope<CharT, Traits, Allocator>::value_type>
558 basic_rope<CharT, Traits, Allocator>::to_string()
559 {
560 if (!impl_)
561 detail::throw_bad_alloc();
562
563 std::basic_string<typename basic_rope<CharT, Traits, Allocator>::value_type> out{};
564
565 size_t total{};
566
567 for (auto p = impl_; p; p = p->right_ ? p->right_->impl_ : nullptr)
568 total += p->capacity_;
569
570 out.reserve(total);
571
572 auto start = impl_;
573
574 while (start)
575 {
576 out.append(start->blob_, start->capacity_);
577
578 if (start->right_)
579 start = start->right_->impl_;
580 else
581 break;
582 }
583
584 return std::move(out);
585 }
586
587 template <class CharT, class Traits, class Allocator>
588 std::basic_string<typename basic_rope<CharT, Traits, Allocator>::value_type>
589 basic_rope<CharT, Traits, Allocator>::to_string() const
590 {
591 return this->to_string();
592 }
593
594 template <class CharT, class Traits, class Allocator>
595 bool basic_rope<CharT, Traits, Allocator>::operator!=(const boost::core::basic_string_view<CharT>& str)
596 {
597 return !(*this == str);
598 }
599
600 template <class CharT, class Traits, class Allocator>
601 basic_rope<CharT, Traits, Allocator>::rope_ptr basic_rope<CharT, Traits, Allocator>::concat(rope_ptr right)
602 {
603 if (!impl_)
604 detail::throw_bad_alloc();
605
606 return impl_->concat(this, right);
607 }
608
609 template <class CharT, class Traits, class Allocator>
610 basic_rope<CharT, Traits, Allocator>::rope_ptr basic_rope<CharT, Traits, Allocator>::insert(size_type pos,
611 const boost::core::basic_string_view<CharT>& text,
612 rope_ptr left) const
613 {
614 if (!impl_)
615 detail::throw_bad_alloc();
616
617 return impl_->insert(pos, text, left);
618 }
619 template <class CharT, class Traits, class Allocator>
620
621 basic_rope<CharT, Traits, Allocator>::pointer basic_rope<CharT, Traits, Allocator>::operator*() const
622 {
623 return this->to_string();
624 }
625
626 template <class CharT, class Traits, class Allocator>
627 basic_rope<CharT, Traits, Allocator>::rope_ptr basic_rope<CharT, Traits, Allocator>::operator++()
628 {
629 return impl_->right_;
630 }
631
632 template <class CharT, class Traits, class Allocator>
633 basic_rope<CharT, Traits, Allocator>::rope_ptr basic_rope<CharT, Traits, Allocator>::operator++(int n)
634 {
635 rope_ptr ret{};
636 while (n && ret)
637 {
638 ret = ret->operator++();
639 --n;
640 }
641
642 return ret;
643 }
644
645 template <class CharT, class Traits, class Allocator>
646 basic_rope<CharT, Traits, Allocator>::rope_ptr basic_rope<CharT, Traits, Allocator>::operator--()
647 {
648 return impl_->left_;
649 }
650
651 template <class CharT, class Traits, class Allocator>
652 basic_rope<CharT, Traits, Allocator>::rope_ptr basic_rope<CharT, Traits, Allocator>::operator--(int n)
653 {
654 rope_ptr ret{};
655 while (n && ret)
656 {
657 ret = ret->operator--();
658 --n;
659 }
660
661 return ret;
662 }
663
664} // namespace ocl::tproc
665
666#endif
This class implements a rope type for any CharT type.
Definition rope_fwd.hpp:23
AMLALE: SourcePair desrves documentation by themselves.
Definition bfs.hpp:13
PImpl of the rope algorithm.
Definition rope_fwd.inl:18