7 /*======================================================================*//**
9 One line of text is stored in this class, with additional metadata to track
10 whether the text was terminated by an EoL (End of Line) sequence, and, if so,
11 what the EoL sequence was.
13 This is particularly useful for reading lines of text from a file or a socket
14 without having to go to additional efforts to track whether the final line is
15 terminated with an EoL sequence (since this class takes care of this detail).
17 By default, the EoL sequence may be <CR>, <LF>, <CRLF>, or
21 One of the challenges with the @c std::string class is that it doesn't
22 provide a way to differentiate between a empty line and @c NULL, unless one
23 uses pointers to @c std::string objects and sets thoes pointers to @c nullptr
24 which entails having to test for that, resulting in more code in various
25 places to test for multiple conditions, thus providing opportunities for more
26 unintended situations (a.k.a., bugs) ... and then there's the matter of how
27 to handle the variety of EoL sequences that persist due to the diversity of
28 Operating System standards, which complicates matters even more.
30 This class handles all of these details gracefully, and provides a thorough
31 variety of clearly-documented methods that ensure consistency whilst handling
32 the complexity of EoL sequences that may be 1 or 2 characters long and also
33 differentiating between blank and NULL lines, all without causing significant
34 increases in complexity in code.
37 - 2024-Nov-16 v1.00 Initial version
38 - 2025-Jan-05 v1.00 Added c_str(), data(), and append(char*, int) methods,
39 and the rline(std::string, std::string) constructor
40 - 2025-Jan-09 v1.00 Added @ref eol(char, char) and @ref eol(std::string)
41 methods for configuring a specific EoL sequence
42 - 2025-Jan-12 v1.00 Changed std::length_error to std::invalid_argument in
43 response to length being specified as a negative value
44 so this can more easily be handled with seperate code;
45 added the @c at() method, and [], ==, and != operators
46 - 2025-Jan-13 v1.00 Improved internal support for EoL flag indication
48 @author Randolf Richardson
49 *///=========================================================================
52 std::string __line; // Text data without EoL sequence
53 char __eol_detected[3] = {'\0', '\0', '\0'}; // EoL sequence detected (default = none)
54 int __eol_detected_size = 0; // EoL sequence detected size (default = 0)
55 char __eol_standard[3] = {'\0', '\0', '\0'}; // EoL sequence standard (default = none/autodetect)
56 int __eol_standard_size = 0; // EoL sequence standard size (default = 0)
58 /*======================================================================*//**
60 Internal line-of-text intake method used by constructors and other methods.
61 @returns TRUE = EoL sequence was encountered@n
62 FALSE = EoL sequence is not present
63 *///=========================================================================
65 /// Line of text to embrace
67 /// Length of data (-1 = ASCIIZ string)
68 size_t len = -1) noexcept {
70 // --------------------------------------------------------------------------
72 // --------------------------------------------------------------------------
73 if (line == nullptr) return false; // A null string was provided, so we can avoid a lot of processing
75//std::cout << "__rline len=" << __line.size() << " data=[" << data << "]" << std::endl;
76 // --------------------------------------------------------------------------
77 // Measure size of line if an ASCIIZ string was indicated.
78 // --------------------------------------------------------------------------
79 if (len == -1) len = std::strlen(line);
80 if (len == 0) return false; // Empty line provided, so this is an easy finish
82 // --------------------------------------------------------------------------
83 // Pre-allocate memory in std::string so that we can write directly to the
84 // underlying array, which is the most efficient (fastest) way.
85 // --------------------------------------------------------------------------
86 int offset = __line.size();
87 __line.resize(offset + len); // This must be done before calling __line.data()
88 char* __line_data = __line.data();
90//std::cout << "__rline len=" << len << " offset=" << offset << " line=[" << line << "]" << std::endl;
91//std::cout << "__rline len=" << len << " offset=" << offset << std::endl;
92 // --------------------------------------------------------------------------
93 // Detect existing (as in already-defined) EoL sequence while copying data.
94 // --------------------------------------------------------------------------
95 if (__eol_standard_size != 0) {
96//std::cout << "__eol_sequence_size != 0" << std::endl;
98 // --------------------------------------------------------------------------
99 // Internal variables.
100 // --------------------------------------------------------------------------
102 char e = __eol_standard[0];
105 // --------------------------------------------------------------------------
106 // Check for edge case with 2-character EoL sequence carries over from
107 // existing line data.
108 // --------------------------------------------------------------------------
109 if (offset > 0 && __eol_standard_size == 2) {
110 if (__line[offset - 1] == e && line[0] == __eol_standard[1]) {
111 __line.resize(__line.size() - 1); // Crop previous character
112 __eol_detected[0] = e;
113 __eol_detected[1] = line[0];
114 __eol_detected_size = __eol_standard_size;
117 } // -x- if !offset -x-
119//std::cout << "__rline len=" << len << " offset=" << offset << " line=[" << line << "]" << std::endl;
120 // --------------------------------------------------------------------------
122 // --------------------------------------------------------------------------
123 for (i = 0; i < len; i++) {
125 // --------------------------------------------------------------------------
126 // Obtain next character to absorb.
127 // --------------------------------------------------------------------------
130 // --------------------------------------------------------------------------
131 // EoL sequence detected. If this is a one-character EoL sequence, then it
132 // matches and we're done; or if this is a two-character EoL sequence, then
133 // it also must match; otherwise the sequence will be ignored and treated as
134 // a normal part of the text.
135 // --------------------------------------------------------------------------
137 if (__eol_standard_size == 1) {
138 __eol_detected[0] = __eol_standard[0];
139 __eol_detected[1] = __eol_standard[1];
140 __eol_detected_size = 1;
143 if ((i + 1) < len && line[i + 1] == __eol_standard[1]) {
144 __eol_detected[0] = __eol_standard[0];
145 __eol_detected[1] = __eol_standard[1];
146 __eol_detected_size = 2;
152 // --------------------------------------------------------------------------
153 // Append character to internal __line by writing directly to the underlying
154 // array (which is the most efficienty way to store it), then advance the
156 // --------------------------------------------------------------------------
157 __line_data[offset++] = c;
161 // --------------------------------------------------------------------------
162 // Automatic EoL sequence detection while copying data.
163 // --------------------------------------------------------------------------
165//std::cout << "__eol_sequence_size == 0" << std::endl;
167 // --------------------------------------------------------------------------
168 // Internal variables.
169 // --------------------------------------------------------------------------
173//std::cout << "__rline len=" << len << " offset=" << offset << " line=[" << line << "]" << std::endl;
174 // --------------------------------------------------------------------------
176 // --------------------------------------------------------------------------
177 for (i = 0; i < len; i++) {
179 // --------------------------------------------------------------------------
180 // Obtain next character to absorb.
181 // --------------------------------------------------------------------------
184 // --------------------------------------------------------------------------
185 // LF detected, so save it and exit the loop, while also advancing
186 // __eol_sequence_size (in two places if this is a broken LFCR sequence). We
187 // do need to compare "i" against "len" to prevent a 1-byte index overflow.
188 // --------------------------------------------------------------------------
189 if (c == 10) { // LF detected
190 __eol_detected[__eol_detected_size++] = __eol_standard[__eol_standard_size++] = c;
191 if ((i + 1) < len && line[i + 1] == 13) { // CR detected, so save it too
192 __eol_detected[__eol_detected_size++] = __eol_standard[__eol_standard_size++] = 13;
194//std::cout << "__eol_detected_size<LF>=" << __eol_detected_size << std::endl;
197 // --------------------------------------------------------------------------
198 // CR detected, so save it and exit the loop, while also advancing
199 // __eol_sequence_size (in two places if this is a valid CRLF sequence). We
200 // do need to compare "i" against "len" to prevent a 1-byte index overflow.
201 // --------------------------------------------------------------------------
202 } else if (c == 13) { // CR detected
203 __eol_detected[__eol_detected_size++] = c;
204 __eol_standard[__eol_standard_size++] = c;
205 if ((i + 1) < len && line[i + 1] == 10) { // LF detected, so save it too
206 __eol_detected[__eol_detected_size++] = 10; // LF detected, so save it too
207 __eol_standard[__eol_standard_size++] = 10; // LF detected, so save it too
209//std::cout << "__eol_detected_size<CR>=" << __eol_detected_size << std::endl;
212 } // -x- if CR/LF -x-
214 // --------------------------------------------------------------------------
215 // Append character to internal __line by writing directly to the underlying
216 // array (which is the most efficienty way to store it), then advance the
218 // --------------------------------------------------------------------------
219 __line_data[offset++] = c;
220//std::cout << "c=" << c << " offset=" << offset << std::endl;
224 } // -x- if !__eol_standar_size -x-
226 // --------------------------------------------------------------------------
227 // The "offset" variable was updated by the loop to ensure it holds the
228 // accurate length of __line internally. The "i" variable is loop-local.
229 // --------------------------------------------------------------------------
230 __line.resize(offset);
231//std::cout << "offset=" << offset << " __line=[" << get() << "]" << std::endl;
233 return __eol_standard_size > 0; // TRUE = EoL sequence was encountered
234 } // -x- void __rline -x-
237 /*======================================================================*//**
239 Instantiate an empty rline, with no EoL sequences present.
240 *///=========================================================================
241 rline() noexcept {} // -x- constructor rline -x-
243 /*======================================================================*//**
245 Instantiate a new rline based on the supplied line of text, ending early upon
246 encountering the first EoL sequence (use either the @ref size(bool) or the
247 @ref size_with_eol() method to obtain the total quantity of characters that
248 were absorbed, including the EoL sequence {if present}).
250 If no EoL sequence was encountered, the @ref has_eol() method will return
251 `false` even though the contents of the supplied line were copied (and are
252 still accessible) -- this is useful in loops reading lines of text that are
253 too long to store in memory with a single allocation (and either interpreting
254 the absorbed data somehow, or using a simple loop to skip through to the end
255 of the line before issuing an error that indicates the total line length).
257 If @c line is a @c nullptr then it will be treated as an empty string without
258 an EoL sequence, instead of throwing a Null Pointer Exception. This provides
259 an opportunity to write simpler code (see the @ref is_null method for the way
260 to check for this condition, which also happens to be the result of using the
261 default constructor or calling @ref clear).
268 *///=========================================================================
270 /// Line of text to embrace
272 /// Length of line (-1 == ASCIIZ string)
273 size_t len = -1) noexcept {
275 } // -x- constructor rline -x-
277 /*======================================================================*//**
279 Instantiate a new rline based on the supplied line of text, ending early upon
280 encountering the first EoL sequence (use either the `size(true)` or the
281 `size_full()` method to obtain the total quantity of characters that were
282 absorbed, including the EoL sequence {if present}).
284 If no EoL sequence was encountered, the @ref has_eol() method will return
285 `false` even though the contents of the supplied line were copied (and are
286 still accessible) -- this is useful in loops reading lines of text that are
287 too long to store in memory with a single allocation (and either interpreting
288 the absorbed data somehow, or using a simple loop to skip through to the end
289 of the line before issuing an error that indicates the total line length).
295 *///=========================================================================
297 /// Line of text to embrace
299 /// Length of line (-1 == defer to length of line provided)
300 size_t len = -1) noexcept {
301 __rline(line.data(), len == -1 ? line.length() : len);
302 } // -x- constructor rline -x-
304 /*======================================================================*//**
306 Instantiate a new rline based on the supplied line of text, ending early upon
307 encountering the first EoL sequence (the `size(true)` and `size_full()`
308 methods obtain the total quantity of characters that were absorbed, including
309 the EoL sequence {if present}).
311 If no EoL sequence was encountered, the @ref has_eol() method will return
312 `false` even though the contents of the supplied line were copied (and are
313 still accessible) -- this is useful in loops reading lines of text that are
314 too long to store in memory with a single allocation (and either interpreting
315 the absorbed data somehow, or using a simple loop to skip through to the end
316 of the line before issuing an error that indicates the total line length).
317 @throws std::length_error if the EoL sequence is longer than 2 characters
323 *///=========================================================================
325 /// Line of text to embrace, without the EoL sequence characters
327 /// The EoL sequence to impose as if it had been detected (may be only 1 or 2
329 std::string eol_sequence) {
331 // --------------------------------------------------------------------------
332 // Save EoL sequence while also performing a simple syntax check.
333 // --------------------------------------------------------------------------
334 switch (eol_sequence.size()) {
336 __eol_detected[0] = __eol_standard[0] = eol_sequence[0];
337 __eol_detected[1] = __eol_standard[1] = eol_sequence[1];
338 __eol_detected_size = __eol_standard_size = 2;
341 __eol_detected[0] = __eol_standard[0] = eol_sequence[0];
342 __eol_detected[1] = __eol_standard[1] = '\0';
343 __eol_detected_size = __eol_standard_size = 1;
346 __eol_detected[0] = __eol_standard[0] = '\0';
347 __eol_detected[1] = __eol_standard[1] = '\0';
348 __eol_detected_size = __eol_standard_size = 0;
350 deafult: // Any other length is invalid
351 throw std::length_error("eol_sequence is too long (maximum length is 2 characters)");
352 } // -x- switch eol_sequence.size -x-
354 // --------------------------------------------------------------------------
356 // --------------------------------------------------------------------------
357 __rline(line.data(), line.length());
359 } // -x- constructor rline -x-
361 /*======================================================================*//**
363 Add the specified string (without checking whether it contains any EoL
364 character sequences) onto the line of text (before the EoL sequence).
365 @returns The same rline object so as to facilitate stacking
368 *///=========================================================================
370 /// The string to insert
372 /// Length of line (-1 == ASCIIZ string)
375 // --------------------------------------------------------------------------
376 // Measure size of line if an ASCIIZ string was indicated.
377 // --------------------------------------------------------------------------
378// if (len == -1) len = std::strlen(line);
379// if (len == 0) return this; // Empty line provided, so do nothing
381 // --------------------------------------------------------------------------
382 // Add to line of text.
383 // --------------------------------------------------------------------------
384// __line.append(line, len);
386 // --------------------------------------------------------------------------
388 // --------------------------------------------------------------------------
389//std::cout << "__rline append()*=[" << line << "]" << " len=" << len << std::endl;
391//std::cout << " __line=[" << __line << "]" << std::endl;
394 } // -x- rline* append -x-
396 /*======================================================================*//**
398 Add the specified string (without checking whether it contains any EoL
399 character sequences) onto the line of text (before the EoL sequence).
400 @returns The same rline object so as to facilitate stacking
403 *///=========================================================================
405 /// The string to insert
406 const std::string line,
407 /// Length of line (-1 == defer to length of line provided)
409 //__line.append(line, len == -1 ? line.length() : len);
410//std::cout << "__rline append()=[" << line << "]" << " len=" << len << std::endl;
411 __rline(line.data(), len == -1 ? line.length() : len);
413 } // -x- rline* append -x-
415 /*======================================================================*//**
417 Assign a new rline based on the supplied line of text, ending early upon
418 encountering the first EoL sequence (use either the @ref size(bool) or the
419 @ref size_with_eol() method to obtain the total quantity of characters that
420 were absorbed, including the EoL sequence {if present}).
422 If no EoL sequence was encountered, the @ref has_eol() method will return
423 `false` even though the contents of the supplied line were copied (and are
424 still accessible) -- this is useful in loops reading lines of text that are
425 too long to store in memory with a single allocation (and either interpreting
426 the absorbed data somehow, or using a simple loop to skip through to the end
427 of the line before issuing an error that indicates the total line length).
429 If @c line is a @c nullptr then it will be treated as an empty string without
430 an EoL sequence, instead of throwing a Null Pointer Exception. This provides
431 an opportunity to write simpler code (see the @ref is_null method for the way
432 to check for this condition, which also happens to be the result of using the
433 default constructor or calling @ref clear).
434 @returns The same rline object so as to facilitate stacking
442 *///=========================================================================
444 /// Line of text to embrace
446 /// Length of line (-1 == ASCIIZ string)
447 size_t len = -1) noexcept {
449 // --------------------------------------------------------------------------
450 // Reset internal variables.
451 // --------------------------------------------------------------------------
452 __eol_detected[0] = __eol_standard[0] = '\0';
453 __eol_detected[1] = __eol_standard[1] = '\0';
454 __eol_detected_size = __eol_standard_size = 0;
457 // --------------------------------------------------------------------------
458 // Replace line of text.
459 // --------------------------------------------------------------------------
463 } // -x- rline* assign -x-
465 /*======================================================================*//**
467 Assign a new rline based on the supplied line of text, ending early upon
468 encountering the first EoL sequence (use either the @ref size(bool) or the
469 @ref size_with_eol() method to obtain the total quantity of characters that
470 were absorbed, including the EoL sequence {if present}).
472 If no EoL sequence was encountered, the @ref has_eol() method will return
473 `false` even though the contents of the supplied line were copied (and are
474 still accessible) -- this is useful in loops reading lines of text that are
475 too long to store in memory with a single allocation (and either interpreting
476 the absorbed data somehow, or using a simple loop to skip through to the end
477 of the line before issuing an error that indicates the total line length).
478 @returns The same rline object so as to facilitate stacking
485 *///=========================================================================
487 /// Line of text to embrace
489 /// Length of line (-1 == defer to length of line provided)
493 // --------------------------------------------------------------------------
494 // Reset internal variables.
495 // --------------------------------------------------------------------------
496 __eol_detected[0] = __eol_standard[0] = '\0';
497 __eol_detected[1] = __eol_standard[1] = '\0';
498 __eol_detected_size = __eol_standard_size = 0;
501 // --------------------------------------------------------------------------
502 // Replace line of text.
503 // --------------------------------------------------------------------------
504 __rline(line.data(), len == -1 ? line.length() : len);
507 } // -x- rline* assign -x-
509 /*======================================================================*//**
511 Array-style access to the underlying std::string (without the EoL sequence).
513 The first element is at index 0.
514 @throws std::out_of_range if the index is out-of-range
517 *///=========================================================================
519 /// Index of character to access (0 = first element; negative index values
520 /// are calculated in reverse, starting with -1 as the final position)
523 // --------------------------------------------------------------------------
524 // Internal variables.
525 // --------------------------------------------------------------------------
526 int MAX = __line.length();
528 // --------------------------------------------------------------------------
530 // --------------------------------------------------------------------------
531 if (index >= MAX || 0 - index > MAX) throw std::out_of_range("index (position=" + std::to_string(index) + ") falls outside of the underlying string (length=" + std::to_string(MAX) + ")");
533 return __line[index >= 0 ? index : MAX - index];
536 /*======================================================================*//**
538 Reset all data and internal flags to the original state of the constructor
539 that has no parameters.
540 @returns The same rline object so as to facilitate stacking
541 *///=========================================================================
542 rline* clear() noexcept {
544 // --------------------------------------------------------------------------
545 // Reset internal variables.
546 // --------------------------------------------------------------------------
547 __eol_detected[0] = __eol_standard[0] = '\0';
548 __eol_detected[1] = __eol_standard[1] = '\0';
549 __eol_detected_size = __eol_standard_size = 0;
553 } // -x- rline* clear -x-
555 /*======================================================================*//**
557 Returns a pointer to the underlying string array, without the EoL sequence.
558 @returns Pointer to buffer (null terminated)
560 *///=========================================================================
561 const char* c_str() noexcept {
562 return __line.c_str();
563 } // -x- char* c_str -x-
565 /*======================================================================*//**
567 Returns a pointer to the underlying string array, without the EoL sequence.
568 @returns Pointer to buffer (not null terminated)
570 *///=========================================================================
571 char* data() noexcept {
572 return __line.data();
573 } // -x- char* data -x-
575 /*======================================================================*//**
577 Returns a pointer to the underlying std::string object, without the EoL
579 @returns Pointer to buffer (not null terminated)
581 *///=========================================================================
582 std::string* data_string() noexcept {
584 } // -x- std::string* data -x-
586 /*======================================================================*//**
588 Indicate whether the line of text is empty. Whether an EoL sequence is
589 present is incidental since this only measures the size of the line of text
590 without regard for the presence of an EoL sequence.
591 @returns TRUE = Line of text is empty@n
592 FALSE = Line of text is not empty
595 *///=========================================================================
596 bool empty() noexcept {
597 return __line.empty();
598 } // -x- bool empty -x-
600 /*======================================================================*//**
602 Obtain a copy of the EoL sequence that was detected.
603 @returns The EoL sequence as a string
605 *///=========================================================================
606 const std::string eol(
607 /// TRUE = return the detected EoL sequence (default) @n
608 /// FALSE = return the EoL sequence to use when detecting an EoL sequence
609 const bool detected = true) noexcept {
610 return detected ? __eol_detected : __eol_standard;
611 } // -x- std::string eol -x-
613 /*======================================================================*//**
615 Define the EoL sequence to use when detecting an EoL sequence (also clears
616 the detected EoL sequence, if present).
618 This method is the most efficient @c eol method to use for defining the
619 specific EoL sequence to use.
620 @returns The same rline object so as to facilitate stacking
622 *///=========================================================================
624 /// The first character of the EoL sequence (typically 10 or 13)@c
625 /// 10 = LF (Line Feed) @n
626 /// 13 = CR (Carriage Return) @n
627 /// 0 = enable EoL sequence auto-detection (this is the default in all
628 /// constructors, and is also set by the @ref clear method)
630 /// The second character of the EoL sequence (typically 10 or 13)@c
631 /// 10 = LF (Line Feed) @n
632 /// 13 = CR (Carriage Return) @n
633 /// 0 = indicate that this is not a two-character EoL sequence
634 const char c2 = 0) noexcept {
636 // --------------------------------------------------------------------------
637 // Save EoL sequence.
638 // --------------------------------------------------------------------------
639 __eol_detected[0] = __eol_detected[1] = 0;
640 __eol_standard[0] = c1;
641 __eol_standard[1] = c1 == 0 ? 0 : c2;
643 // --------------------------------------------------------------------------
644 // Calculate the size of the EoL sequence.
645 // --------------------------------------------------------------------------
646 __eol_detected_size = 0;
647 __eol_standard_size = 2;
648 if (c2 == 0) __eol_standard_size = 1;
649 if (c1 == 0) __eol_standard_size = 0;
652 } // -x- rline* eol -x-
654 /*======================================================================*//**
656 Define the EoL sequence to use when detecting an EoL sequence (also clears
657 the detected EoL sequence, if present).
658 @throws std::length_error if the EoL sequence is longer than 2 characters
659 @returns The same rline object so as to facilitate stacking
661 *///=========================================================================
663 /// The EoL sequence (may be only 1 or 2 characters long; or 0 characters to
664 /// enable EoL auto-detection, which is also the default in all constructors)
665 const std::string eol_sequence) {
667 // --------------------------------------------------------------------------
668 // Save EoL sequence while also performing a simple syntax check.
669 // --------------------------------------------------------------------------
670 switch (eol_sequence.size()) {
672 __eol_detected[0] = __eol_detected[1] = 0;
673 __eol_standard[0] = eol_sequence[0];
674 __eol_standard[1] = eol_sequence[1];
675 __eol_detected_size = 0;
676 __eol_standard_size = 2;
679 __eol_detected[0] = __eol_detected[1] = 0;
680 __eol_standard[0] = eol_sequence[0];
681 __eol_standard[1] = '\0';
682 __eol_detected_size = 0;
683 __eol_standard_size = 1;
686 __eol_detected[0] = __eol_detected[1] = 0;
687 __eol_standard[0] = '\0';
688 __eol_standard[1] = '\0';
689 __eol_detected_size = 0;
690 __eol_standard_size = 0;
692 deafult: // Any other length is invalid
693 throw std::length_error("eol_sequence is too long (maximum length is 2 characters)");
694 } // -x- switch eol_sequence.size -x-
697 } // -x- rline* eol -x-
699 /*======================================================================*//**
701 Obtain a copy of the entire line of text, without the EoL sequence (albeit by
702 default, this can also be included by setting the @c include_eol flag).
703 @returns The line of text as a string
705 *///=========================================================================
706 const std::string get(
707 /// Whether to include the EoL detected (default = FALSE)
708 const bool include_eol = false) noexcept {
709 return include_eol ? __line + __eol_detected : __line;
710 } // -x- std::string get -x-
712 /*======================================================================*//**
714 Indicate whether an EoL sequence was detected.
715 @returns TRUE = EoL sequence is present@n
716 FALSE = No EoL sequence
718 *///=========================================================================
719 bool has_eol() noexcept {
720 return __eol_detected_size != 0;
721 } // -x- bool has_eol -x-
723 /*======================================================================*//**
725 Indicate whether a broken EoL sequence detected of `<LFCR>` is being used as
727 @returns TRUE = Broken EoL sequence is present@n
728 FALSE = No broken EoL sequence
730 *///=========================================================================
732 /// TRUE = report on whether a broken EoL sequence was detected (default) @n
733 /// FALSE = report on whether a broken EoL sequence was specifically defined
734 /// as the EoL sequence to detect
735 const bool detected = true) noexcept {
736 if (true) return __eol_detected_size == 2 && __eol_detected[0] == 10 && __eol_detected[1] == 13;
737 else return __eol_standard_size == 2 && __eol_standard[0] == 10 && __eol_standard[1] == 13;
738 } // -x- bool has_LFCR -x-
740 /*======================================================================*//**
742 Insert the specified string (without checking whether it contains any EoL
743 character sequences) into the line of text at the specified position.
744 @exception std::out_of_range When the position (@c pos) provided is out of
745 range (e.g., position is larger than the entire
747 @returns The same rline object so as to facilitate stacking
750 *///=========================================================================
752 /// Where to insert the supplied string (0 = insert before first character;
753 /// and use a negative number to set the position relative to the end of the
755 int position, // Must be "int" and not "size_t" because size_t is unsigned
756 /// The string to insert
757 const std::string str,
758 /// Length of string (-1 == defer to length of string provided)
760 __line.insert(position >= 0 ? position : (__line.size() + position),
761 len == -1 ? str : str.substr(0, len));
763 } // -x- rline* insert -x-
765 /*======================================================================*//**
767 Indicate whether this line is non-existent, which means there is no text and
768 no detected EoL sequence. This is less than an an @ref empty() string when
769 it confirms that there literally are no characters at all.
770 @returns TRUE = null string@n
771 FALSE = not null (has text and/or an EoL
774 *///=========================================================================
775 bool is_null() noexcept {
776 return (__line.size() + __eol_detected_size) == 0;
777 } // -x- bool is_null -x-
779 /*======================================================================*//**
781 Provides the length of the line of text without the EoL sequence.
783 This method is identital to the @ref size method.
784 @returns Number of bytes
786 *///=========================================================================
788 /// Whether to include the EoL sequence (default = FALSE)
789 const bool include_eol = false) noexcept {
790 return include_eol ? __line.size() + __eol_detected_size : __line.size();
791 } // -x- size_t length -x-
793 /*======================================================================*//**
795 Provides the length of the line of text without the EoL sequence.
797 This method is identital to the @ref length method.
798 @returns Number of bytes
802 *///=========================================================================
804 /// Whether to include the EoL sequence (default = FALSE)
805 const bool include_eol = false) noexcept {
806 return include_eol ? __line.size() + __eol_detected_size : __line.size();
807 } // -x- size_t size -x-
809 /*======================================================================*//**
811 Provides the length of the detected EoL sequence.
812 @returns Number of bytes (0 = no EoL string)
814 *///=========================================================================
816 /// TRUE = report on the size of the detected EoL sequence (default) @n
817 /// FALSE = report on the size of the EoL sequence to use when detecting an
819 const bool detected = true) noexcept {
820 return detected ? __eol_detected_size : __eol_standard_size;
821 } // -x- size_t size_eol -x-
823 /*======================================================================*//**
825 Provides the length of the line of text, including the EoL sequence (if one
827 @returns Number of bytes
830 *///=========================================================================
831 size_t size_with_eol() noexcept {
832 return __line.size() + __eol_detected_size;
833 } // -x- size_t size_with_eol -x-
835 /*======================================================================*//**
837 Convert this @c rline to an @c std::string without the EoL sequence.
840 #include <iostream> // std::cout, std::cerr, std::endl, etc.
841 #include <string> // std::string
842 #include <randolf/rline>
844 int main(int argc, char *argv[]) {
845 randolf::rline rl("This is an example.\n");
847 std::cout << "\"" << s << "\"" << std::endl;
849 } // -x- int main -x-
852 @returns The line of text as an std::string object
854 *///=========================================================================
855 operator std::string() const noexcept {
857 } // -x- operator std::string -x-
859 /*======================================================================*//**
861 Compare with the underlying std::string (without the EoL sequence).
862 @returns TRUE = doesn't match@n
864 *///=========================================================================
866 /// String to compare with
867 const std::string s) {
869 } // -x- bool operator== -x-
871 /*======================================================================*//**
873 Compare with the underlying std::string (without the EoL sequence).
874 @returns TRUE = match@n
875 FALSE = doesn't match
876 *///=========================================================================
878 /// String to compare with
879 const std::string s) {
881 } // -x- bool operator== -x-
883 /*======================================================================*//**
885 Array-style access to the underlying std::string (without the EoL sequence).
887 The first element is at index 0.
888 @throws std::out_of_range if the index is out-of-range
891 *///=========================================================================
893 /// Index of character to access (0 = first element; negative index values
894 /// are calculated in reverse, starting with -1 as the final position)
897 // --------------------------------------------------------------------------
898 // Internal variables.
899 // --------------------------------------------------------------------------
900 int MAX = __line.length();
902 // --------------------------------------------------------------------------
904 // --------------------------------------------------------------------------
905 if (index >= MAX || 0 - index > MAX) throw std::out_of_range("index (position=" + std::to_string(index) + ") falls outside of the underlying string (length=" + std::to_string(MAX) + ")");
907 return __line[index >= 0 ? index : MAX - index];
908 } // -x- char operator[] -x-
910 /*======================================================================*//**
912 Support convenient streaming usage with std::cout, std::cerr, and friends.
915 #include <iostream> // std::cout, std::cerr, std::endl, etc.
916 #include <string> // std::string
917 #include <randolf/rline>
919 int main(int argc, char *argv[]) {
920 randolf::rline rl("This is an example.\n");
921 std::cout << "\"" << rl << "\"" << std::endl;
923 } // -x- int main -x-
926 @returns Line of text, without EoL sequence
927 *///=========================================================================
928 friend std::ostream& operator<< (
929 /// Output stream (provided automatically by std::cout and std::cerr)
931 /// Object class (matched by compiler)
933 return o << (char*)c.__line.data();
934 } // -x- std::ostream& operator<< -x-
936 }; // -x- class rline -x-
938}; // -x- namespace randolf -x-