randolf.ca  1.00
Randolf Richardson's C++ classes
Loading...
Searching...
No Matches
rring
1#pragma once
2
3#include <cstring>
4#include <mutex>
5#include <new>
6#include <tuple>
7#include <vector>
8
9#include <randolf/rring_bam>
10
11namespace randolf {
12
13 /*======================================================================*//**
14 @brief
15 Ring buffer implementation for 8-bit bytes that's resizeable and threadsafe,
16 and the methods are designed to be efficient in various ways, including by
17 supporting the transfer of multiple bytes in variable quantities.
18
19 Data is stored internally as a @c char* array, but a few methods are provided
20 to also support easy interoperability with @c std::vector objects since there
21 are many scenarios where this can be helpful (and from the perspective of
22 optimization, this eliminates the cumbersome need to utilize conversions
23 between @c std::string and @c std::vector objects since, internally, we use
24 direct access to/from @c std::vector objects).
25
26 @par Use case
27 Occasionally, ring buffers (a.k.a., circular buffers) are useful in specific
28 situations because they provide better performance compared to other types of
29 buffers, which either results in frequent allocations and deallocations of
30 memory for transient-but-ordered portions of data or tracking sections of
31 transient-but-ordered portions of data. A ring buffer relies on a single
32 allocation of memory, along with a few variables for tracking top and bottom
33 positions plus a few other internals to ensure smooth and accurate usage.
34
35 This class handles all of these details gracefully, and provides a thorough
36 variety of clearly-documented methods that ensure consistency whilst handling
37 the complexity of EoL sequences that may be 1 or 2 characters long and also
38 differentiating between blank and NULL lines, all without causing significant
39 increases in complexity in code.
40
41 @par Conventions
42 Lower-case letters "rb" are regularly used in partial example code to
43 represent an instantiated rring object.
44
45 An ASCIIZ string is a C-string (char* array) that includes a terminating null
46 (0) character at the end.
47
48 @par History
49 - 2024-Nov-25 v1.00 Initial version
50 - 2024-Dec-03 v1.00 Added @ref rring_bam structure integration
51 - 2024-Dec-03 v1.00 Added @ref data_bam and related methods
52 @version 1.00
53 @author Randolf Richardson
54 *///=========================================================================
55 class rring {
56 private:
57 char* __buffer; // Pointer to buffer
58 size_t __size; // Size of buffer
59 size_t __head = 0; // Head (beginning)
60 size_t __tail = 0; // Tail (end)
61 bool __wipe_policy = true; // Destructor memory wipe-out policy
62 std::mutex __mutex; // Used to add support for multiple threads
63
64 public:
65 /*======================================================================*//**
66 @brief
67 Instantiate an empty ring buffer (which can be re-sized or replaced later).
68 *///=========================================================================
69 rring() noexcept {
70 __buffer = nullptr;
71 __size = 0;
72 }; // -x- constructor rring -x-
73
74 /*======================================================================*//**
75 @brief
76 Instantiate an empty ring buffer (which can be re-sized later).
77 @exception std::bad_alloc When @c malloc() fails to allocate memory
78 *///=========================================================================
79 rring(
80 /// Size of buffer
81 const size_t buffer_size) {
82 __buffer = (char*)malloc(buffer_size);
83 if (__buffer == nullptr) throw std::bad_alloc();
84 __size = buffer_size;
85 }; // -x- constructor rring -x-
86
87 /*======================================================================*//**
88 @brief
89 Free internal memory resources, after clearing buffer's memory (if the policy
90 indicates that it needs to be cleared).
91 *///=========================================================================
92 ~rring() noexcept {
93 if (__buffer != nullptr) {
94
95 // --------------------------------------------------------------------------
96 // Overwrite buffer's memory with NULL if policy indicates it.
97 // --------------------------------------------------------------------------
98 if (__wipe_policy) while (__size > 0) __buffer[--__size] = '\0';
99
100 // --------------------------------------------------------------------------
101 // Free buffer's memory resource.
102 // --------------------------------------------------------------------------
103 free(__buffer);
104
105 } // -x- if __buffer -x-
106 }; // -x- destructor ~rring -x-
107
108 private:
109 /*======================================================================*//**
110 @brief
111 Calculate the number of available entries in the ring buffer.
112 @returns Number of entries available
113 @see get_available
114 *///=========================================================================
115 size_t __available() noexcept {
116 return (__head < __tail) ? __tail - __head
117 : __size - (__head - __tail);
118 } // -x- size_t __available -x-
119
120 /*======================================================================*//**
121 @brief
122 Calculate the number of utilized entries in the ring buffer.
123 @returns Number of entries utilized
124 @see get_utilized
125 *///=========================================================================
126 size_t __utilized() noexcept {
127 return (__head >= __tail) ? __head - __tail
128 : __size - (__tail - __head);
129 } // -x- size_t __utilized -x-
130
131 public:
132 /*======================================================================*//**
133 @brief
134 Add one character to the unused portion of the internal ring buffer.
135 @exception std::overflow_error If the amount of data exceeds the available
136 free space in the ring buffer
137 @returns The same rring object so as to facilitate stacking
138 @see c_str
139 @see peek
140 @see peek_vector
141 @see remove
142 @see remove_vector
143 *///=========================================================================
144 rring* append(
145 /// Data to add
146 const char data) {
147
148 // --------------------------------------------------------------------------
149 // Add data to ring buffer. If no data is needed, then skip all the mutex
150 // locking overhead, etc., and return the empty string.
151 // --------------------------------------------------------------------------
152 {
153 std::lock_guard<std::mutex> guard(__mutex);
154 if (__available() == 0) throw std::overflow_error("Ring buffer if full");
155 __buffer[__head] = data; // Copy entry
156 if (++__head >= __size) __head = 0; // Wrap-around
157 }
158
159 return this;
160 } // -x- rring* append -x-
161
162 /*======================================================================*//**
163 @brief
164 Add any number of characters that fit within the unused portion of the
165 internal ring buffer.
166 @exception std::length_error If data length is -2 or below
167 @exception std::overflow_error If the amount of data exceeds the available
168 free space in the ring buffer
169 @returns The same rring object so as to facilitate stacking
170 @see c_str
171 @see peek
172 @see remove
173 *///=========================================================================
174 rring* append(
175 /// Data to add
176 const char* data,
177 /// Length of data (-1 = treat @c data as an ASCIIZ string)
178 int len = -1) {
179
180 // --------------------------------------------------------------------------
181 // Syntax checks.
182 // --------------------------------------------------------------------------
183 if (len == -1) len = std::strlen(data);
184 else if (len < -1) throw std::length_error("Unrealistic data length of " + std::to_string(len));
185
186 // --------------------------------------------------------------------------
187 // Add data to ring buffer. If no data is needed, then skip all the mutex
188 // locking overhead, etc., and return the empty string.
189 // --------------------------------------------------------------------------
190 if (len > 0) {
191 std::lock_guard<std::mutex> guard(__mutex);
192 if (__available() < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds available free space in ring buffer");
193 for (int i = 0; i < len; i++) {
194 __buffer[__head] = data[i]; // Copy entry
195 if (++__head >= __size) __head = 0; // Wrap-around
196 } // -x- for i -x-
197 } // -x- if len -x-
198
199 return this;
200 } // -x- rring* append -x-
201
202 /*======================================================================*//**
203 @brief
204 Add any number of characters that fit within the unused portion of the
205 internal ring buffer.
206 @exception std::length_error If data length is -2 or below
207 @exception std::overflow_error If the amount of data exceeds the available
208 free space in the ring buffer
209 @returns The same rring object so as to facilitate stacking
210 @see c_str
211 @see peek
212 @see remove
213 *///=========================================================================
214 rring* append(
215 /// Data to add
216 const std::vector<char> data,
217 /// Length of data (-1 = treat @c data as an ASCIIZ string)
218 int len = -1) {
219
220 // --------------------------------------------------------------------------
221 // Syntax checks.
222 // --------------------------------------------------------------------------
223 if (len == -1) len = data.size();
224 else if (len < -1) throw std::length_error("Unrealistic data length of " + std::to_string(len));
225
226 // --------------------------------------------------------------------------
227 // Add data to ring buffer. If no data is needed, then skip all the mutex
228 // locking overhead, etc., and return the empty string.
229 // --------------------------------------------------------------------------
230 if (len > 0) {
231 std::lock_guard<std::mutex> guard(__mutex);
232 if (__available() < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds available free space in ring buffer");
233 for (int i = 0; i < len; i++) {
234 __buffer[__head] = data[i]; // Copy entry
235 if (++__head >= __size) __head = 0; // Wrap-around
236 } // -x- for i -x-
237 } // -x- if len -x-
238
239 return this;
240 } // -x- rring* append -x-
241
242 /*======================================================================*//**
243 @brief
244 Add any number of characters that fit within the unused portion of the
245 internal ring buffer.
246 @exception std::length_error If data length is -2 or below
247 @exception std::overflow_error If the amount of data exceeds the available
248 free space in the ring buffer
249 @returns The same rring object so as to facilitate stacking
250 @see c_str
251 @see peek
252 @see remove
253 *///=========================================================================
254 rring* append(
255 /// Data to add
256 const std::vector<unsigned char> data,
257 /// Length of data (-1 = treat @c data as an ASCIIZ string)
258 int len = -1) {
259
260 // --------------------------------------------------------------------------
261 // Syntax checks.
262 // --------------------------------------------------------------------------
263 if (len == -1) len = data.size();
264 else if (len < -1) throw std::length_error("Unrealistic data length of " + std::to_string(len));
265
266 // --------------------------------------------------------------------------
267 // Add data to ring buffer. If no data is needed, then skip all the mutex
268 // locking overhead, etc., and return the empty string.
269 // --------------------------------------------------------------------------
270 if (len > 0) {
271 std::lock_guard<std::mutex> guard(__mutex);
272 if (__available() < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds available free space in ring buffer");
273 for (int i = 0; i < len; i++) {
274 __buffer[__head] = data[i]; // Copy entry
275 if (++__head >= __size) __head = 0; // Wrap-around
276 } // -x- for i -x-
277 } // -x- if len -x-
278
279 return this;
280 } // -x- rring* append -x-
281
282 /*======================================================================*//**
283 @brief
284 Array-style access to the utilized portion of the ring buffer's contents,
285 which yields the same result as that of the @ref at() method.
286
287 The first element is at index 0.
288 @throws std::out_of_range if the index is out-of-range
289 @returns char
290 @see operator[]
291 *///=========================================================================
292 char at(
293 /// Index of character to access (0 = first element; negative index values
294 /// are calculated in reverse, starting with -1 as the final position)
295 int index) {
296 std::lock_guard<std::mutex> guard(__mutex);
297
298 // --------------------------------------------------------------------------
299 // Internal variables.
300 // --------------------------------------------------------------------------
301 int MAX = __utilized();
302
303 // --------------------------------------------------------------------------
304 // Syntax checks.
305 // --------------------------------------------------------------------------
306 if (index >= MAX || 0 - index > MAX) throw std::out_of_range("index (position=" + std::to_string(index) + ") falls outside of the utilized portion (length=" + std::to_string(MAX) + ") of the buffer");
307
308 // --------------------------------------------------------------------------
309 // Calculate character's position.
310 // --------------------------------------------------------------------------
311 index = index >= 0 ? __tail + index : __head + index;
312 if (index >= __size) index -= __size;
313
314 return __buffer[index];
315 } // -x- char at -x-
316
317 /*======================================================================*//**
318 @brief
319 Return the contents of the ring buffer as an ASCIIZ string.
320 @post
321 The string returned will need to be `free()`'d after it's no longer needed.
322
323 @code{.cpp}
324 #include <iostream> // std::cout, std::cerr, std::endl, etc.
325 #include <randolf/rring>
326
327 int main(int argc, char *argv[]) {
328 randolf::rring rb(1024);
329 rb.append("This is an example.");
330 char* c = rb.c_str();
331 std::cout << "\"" << c << "\"" << std::endl;
332 free(c);
333 return EXIT_SUCCESS;
334 } // -x- int main -x-
335 @endcode
336
337 @warning
338 When using the @ref data parameter in a multi-threaded scenario, the amount
339 of the memory needed should be set to the maximum size of this ring buffer to
340 prevent unintended under-allocation resulting from a simultaneous increase
341 in utilized ring buffer memory (caused by @ref set_size).
342 @exception std::bad_alloc When @c malloc() fails to allocate memory
343 @returns ASCIIZ string
344 @see append
345 @see peek
346 @see remove
347 @see remove_array
348 *///=========================================================================
349 char* c_str(
350 /// Where to store the ASCIIZ string (including terminating zero, so it is
351 /// imperative that `utilized() + 1` bytes be pre-allocated)@n
352 /// @c nullptr = allocate the needed memory using @c malloc()
353 char* data = nullptr) noexcept {
354
355 // --------------------------------------------------------------------------
356 // Internal variables.
357 // --------------------------------------------------------------------------
358 size_t len;
359
360 // --------------------------------------------------------------------------
361 // Generate ASCIIZ string.
362 // --------------------------------------------------------------------------
363 {
364 std::lock_guard<std::mutex> guard(__mutex);
365 len = __utilized();
366 if (data == nullptr) data = (char*)malloc(len + 1);
367 if (data == nullptr) throw std::bad_alloc();
368 size_t t = __tail;
369 for (int i = 0; i < len; i++) {
370 data[i] = __buffer[t]; // Copy entry
371 if (++t >= __size) t = 0; // Wrap-around
372 } // -x- for i -x-
373 }
374 data[len] = '\0'; // Not "len - 1" because we already allocated "len + 1"
375
376 return data;
377 } // -x- char* c_str -x-
378
379 /*======================================================================*//**
380 @brief
381 Returns a pointer to the underlying ring buffer's array. The beginning of
382 the array is not the same as where the data actually begins (and ends) within
383 the buffer.
384
385 @warning
386 This is the live data, which should not be accessed or modified directly; use
387 the @ref data_bam() and @ref data_bam_adjust() methods instead when there is
388 a need to access or modify the backing array directly.
389 @returns Pointer to buffer (not null terminated)
390 @see get_available
391 @see get_utilized
392 @see set_size
393 @qualifier ADVANCED
394 *///=========================================================================
395 char* data() noexcept {
396 std::lock_guard<std::mutex> guard(__mutex);
397 return __buffer;
398 } // -x- char* data -x-
399
400 /*======================================================================*//**
401 @brief
402 Returns the lengths and addresses of available memory. There may be only one
403 block of memory, or two, depending on whether the available memory straddles
404 the boundary of the ring buffer.
405
406 This is the nearest equivalent of the @ref data() methods provided in classes
407 such as @c std::string and @c std::vector, but with the nuance of supporting
408 the underpinnings of this ring buffer wherein the utilized portions may be
409 stored at both the end and beginning of the buffer memory.
410 @note
411 The intention of this method is to provide an opportunity to directly update
412 the contents of the ring buffer's data section in an efficient manner (e.g.,
413 such as by reading file or socket data directly into memory without having to
414 perform separate data copying operations, etc.).
415 @warning
416 Upon updating a portion of the available-data block, the @c head will also
417 need to be updated accordingly, for which the @ref data_bam_adjust method is
418 provided.
419 @exception std::bad_alloc When @c malloc() fails to allocate memory
420 @returns Pointer to @ref rring_bam structure
421 @see data_bam_adjust
422 @see get_available
423 @see rring_bam
424 @qualifier ADVANCED
425 *///=========================================================================
426 rring_bam* data_bam(
427 /// Pointer to @ref rring_bam structure (that's already been allocated)@n
428 /// @c nullptr (default) = allocate memory for this structure with @c malloc
429 /// (which will need to be `free`'d when it's no longer needed)
430 rring_bam* bam = nullptr) noexcept {
431 std::lock_guard<std::mutex> guard(__mutex);
432
433 // --------------------------------------------------------------------------
434 // Internal variables.
435 // --------------------------------------------------------------------------
436 if (bam == nullptr) {
437 bam = (rring_bam*)malloc(__size);
438 if (bam == nullptr) throw std::bad_alloc();
439 } // -x- if !bam -x-
440
441 // --------------------------------------------------------------------------
442 // Configure utilized data block pointer(s) and length(s).
443 // --------------------------------------------------------------------------
444 bam->block1 = __buffer + __tail;
445 if (__head >= __tail) { // Not wrapped
446 bam->block1_size = __head - __tail;
447 bam->block2 = nullptr;
448 bam->block2_size = 0;
449 } else { // Wrapped
450 bam->block1_size = __size - __tail;
451 bam->block2 = __size - __tail == 0 ? nullptr : __buffer;
452 bam->block2_size = __head;
453 } // -x- if __head -x-
454
455 // --------------------------------------------------------------------------
456 // Configure available data block pointer(s) and length(s).
457 // --------------------------------------------------------------------------
458 bam->avail1 = __buffer + __head;
459 if (__head < __tail) { // Not wrapped
460 bam->avail1_size = __tail - __head;
461 bam->avail2 = nullptr;
462 bam->avail2_size = 0;
463 } else { // Wrapped
464 bam->avail1_size = __size - __head;
465 bam->avail2 = __tail == 0 ? nullptr : __buffer;
466 bam->avail2_size = __tail;
467 } // -x- if __head -x-
468
469 return bam;
470 } // -x- rring_bam* data_bam -x-
471
472 /*======================================================================*//**
473 @brief
474 Arbitrarily change the head and tail positions in the ring buffer. This is
475 intended for use in conjunction with appending data directly to the ring
476 buffer after the @ref data_bam method.
477 @exception std::overflow_error If either adjustment would result in causing
478 an overflow
479 @returns The same rring object so as to facilitate stacking
480 @see data_bam
481 @see get_available
482 @see set_head
483 @see set_tail
484 @qualifier ADVANCED
485 *///=========================================================================
486 rring* data_bam_adjust(
487 /// New internal head position
488 size_t head,
489 /// New internal tail position
490 size_t tail = 0) {
491
492 // --------------------------------------------------------------------------
493 // Set new head and tail positions.
494 // --------------------------------------------------------------------------
495 {
496 std::lock_guard<std::mutex> guard(__mutex);
497 __head = head;
498 __tail = tail;
499 }
500
501 return this;
502 } // -x- rring* data_bam_adjust -x-
503
504 /*======================================================================*//**
505 @brief
506 Reorganizes the utilized memory in the ring buffer memory by moving the tail
507 to the beginning. Defragmentation is completed using a custom-made algorithm
508 that requires only a single pass.
509 @see data_bam
510 @see get_ascii_map
511 @see resize
512 *///=========================================================================
513 rring* defragment(
514 /// Whether to defrag when the utilized block isn't straddling the internal
515 /// memory buffer boundary@n
516 /// TRUE = always defragment (default) @n
517 /// FALSE = only defragment if utilized block straddles memory boundary (this
518 /// favours reducing processor overhead if all that's wanted is to
519 /// have one block of memory to contend with in the BAM, hence it's
520 /// also what the @ref resize() method uses for better performance
521 /// overall because resizing most likely occurs when the need for CPU
522 /// cycles are more critical, so we're doing our part by reducing our
523 /// CPU load)
524 const bool full = true) {
525 {
526 std::lock_guard<std::mutex> guard(__mutex);
527
528 // --------------------------------------------------------------------------
529 // Move data within ring buffer.
530 // --------------------------------------------------------------------------
531 if (!full && __tail > __head) return this; // No work to be done
532 switch (__tail) { // Handle special cases
533 case 1: // Buffer is 1 byte
534 __buffer[0] = __buffer[__tail];
535 __head = 1;
536 __tail = 0;
537 return this;
538 case 0: // Buffer is empty
539 __head = 0;
540 __tail = 0;
541 return this;
542 } // -x- switch __tail -x-
543
544 // --------------------------------------------------------------------------
545 // Internal variables.
546 // --------------------------------------------------------------------------
547 char carry; // Used in swapping elements
548 char last; // Only used if existing
549 size_t len = __utilized();
550
551 // --------------------------------------------------------------------------
552 // Save last entry for remove-and-hold technique so that the array can be
553 // treated as having an even quantity of elements.
554 // --------------------------------------------------------------------------
555 if (len && 1) { // Odd number detected
556 last = __buffer[__tail];
557 len--; // Adjust down to even number
558 } // -x- if odd-len -x-
559
560 // --------------------------------------------------------------------------
561 // Swap characters.
562 // --------------------------------------------------------------------------
563 for (int i = 0; i < len; i++) { // i begins with 0 (even-len) or 1 (odd-len)
564 carry = __buffer[__head] ; // Swap part 1: Carry head
565 __buffer[i] = __buffer[__head]; // Swap part 2: Copy i into head
566 __buffer[__head] = carry; // Swap part 3: Un-carry head into i
567 if (++__head == __size) __head = 0; // Wrap-around
568std::cout << "i=" << i << " __head=" << __head << " carry=" << carry << std::endl;
569 } // -x- for i -x-
570 __head = len;
571 __tail = 0; // Reposition the tail
572
573 // --------------------------------------------------------------------------
574 // Restore last entry for remove-and-hold technique that makes it possible
575 // for the array to be treated as having an even quantity of elements.
576 // --------------------------------------------------------------------------
577 if (len && 1) { // Odd number detected
578 __buffer[__tail] = last;
579 } // -x- if odd-len -x-
580
581 }
582 return this;
583 } // -x- rring* defragment -x-
584
585/*
58656781234 Simple swap (fastest speed)
5871 5
588 2 6
589 3 7
590 4 8
59112345678
592
5935671234 Remove-and-hold last (fastest speed)
594456123
5951 4
596 2 5
597 3 6
5981234567 Re-instate last
599
6005671234 1.5 passes (slowest speed)
6011 5
602 2 6
603 3 7
604 47
605 46
606 45
6071234567
608
6095671234 Carry (medium speed)
6101 4 5
611 2 5 6
612 3 67
6131234567
614*/
615
616 /*======================================================================*//**
617 @brief
618 Discard data from the ring buffer.
619 @returns Number of bytes of data that was discarded
620 @see remove
621 *///=========================================================================
622 size_t discard(
623 /// Number of bytes to discard@n
624 /// -1 = all utilized data that remains will be discarded (default)
625 int len = -1) noexcept {
626 if (len == 0) return 0; // Do nothing
627 size_t discarded;
628 {
629 std::lock_guard<std::mutex> guard(__mutex);
630 if (len == -1) len = __size;
631 else if (len < -1) throw std::length_error("Unrealistic data length of " + std::to_string(len));
632 discarded = __utilized();
633
634 // --------------------------------------------------------------------------
635 // Discard everything because len is greater than or equal to the amount of
636 // data in the ring buffer.
637 // --------------------------------------------------------------------------
638 if (len >= discarded) { // Discard everything if len is too large
639 __head = 0;
640 __tail = 0;
641 return len; // Return quantity that was actually discarded
642 } // -x- if len -x-
643
644 // --------------------------------------------------------------------------
645 // Adjust __tail according to amount of data to discard. Calculating this is
646 // enormously faster than looping through the buffer.
647 // --------------------------------------------------------------------------
648 if (__head >= __tail || __size - __tail < len) { // Not wrapping, so adjustment is easy
649 __tail += len;
650 } else { // Wrapping, so change tail to discard size minus what's discarded from the end
651 __tail = len - (__size - __tail);
652 } // -x- if __head -x-
653 }
654 return discarded - len; // Return quantity that was actually discarded
655
656 } // -x- int discard -x-
657
658 /*======================================================================*//**
659 @brief
660 Indicates whether the ring buffer is empty.
661 @returns TRUE = empty@nFALSE = not empty
662 @see get_available
663 @see get_utilized
664 @see set_size
665 *///=========================================================================
666 bool empty() noexcept {
667 std::lock_guard<std::mutex> guard(__mutex);
668 return __head == __tail;
669 } // -x- bool empty -x-
670
671 /*======================================================================*//**
672 @brief
673 Obtains a one-line ASCII depiction of a map of the ring buffer's memory
674 usage, wherein the utilized and available portions are represented by the `|`
675 (pipe) and '.' (period) characters, respectively (these characters are
676 configurable and can include multiple characters, which is particularly
677 useful for text-mode console output in a Linux shell in which ANSI codes may
678 be used to present these characters in different colours).
679 @exception std::length_error If length is -2 or below
680 @returns One-line ASCII depiction of ring buffer's memory map
681 @see get_available
682 @see get_head
683 @see get_tail
684 @see get_utilized
685 *///=========================================================================
686 std::string get_ascii_map(
687 /// Desired size of ASCII map@n
688 /// -1 = Use size of ring buffer
689 int len = 65,
690 /// Character(s) representing utilized entries
691 const std::string u = "|",
692 /// Character(s) representing available entries
693 const std::string a = ".") noexcept {
694
695 // --------------------------------------------------------------------------
696 // Internal variables, which will be copied .
697 // --------------------------------------------------------------------------
698 double head;
699 double tail;
700 double size;
701 {
702 std::lock_guard<std::mutex> guard(__mutex);
703 head = __head;
704 tail = __tail;
705 size = __size;
706 }
707 std::string map;
708
709 // --------------------------------------------------------------------------
710 // Syntax checks.
711 // --------------------------------------------------------------------------
712 if (len == -1) len = size;
713 if (len == 0) return map;
714 if (len <= -1) throw std::length_error("Unrealistic data length of " + std::to_string(len));
715
716 // --------------------------------------------------------------------------
717 // Generate output.
718 // --------------------------------------------------------------------------
719 if (len >= size) {
720 head = head * (len / size);
721 tail = tail * (len / size);
722 } else {
723 head = head / (size / len);
724 tail = tail / (size / len);
725 } // -x- if len -x-
726 map.reserve(len); // Pre-allocate the exact amount of underlying memory needed
727 if (head >= tail) for (int i = 0; i < len; i++) map.append(i < tail || i >= head ? a : u); // "a" or "u" could be longer than one character
728 else for (int i = 0; i < len; i++) map.append(i < head || i >= tail ? u : a); // "a" or "u" could be longer than one character
729
730 return map;
731 } // -x- std::string get_ascii_map -x-
732
733 /*======================================================================*//**
734 @brief
735 Calculate the number of available entries in the ring buffer.
736 @returns Number of entries available
737 @see get_utilized
738 *///=========================================================================
739 size_t get_available() noexcept {
740 std::lock_guard<std::mutex> guard(__mutex);
741 return __available();
742 } // -x- size_t get_available -x-
743
744 /*======================================================================*//**
745 @brief
746 Obtains the position of the head in the ring buffer.
747 @returns Position of head in ring buffer
748 @see get_available
749 @see get_tail
750 @see get_utilized
751 @see set_head
752 @see set_tail
753 @qualifier ADVANCED
754 *///=========================================================================
755 size_t get_head() noexcept {
756 std::lock_guard<std::mutex> guard(__mutex);
757 return __head;
758 } // -x- size_t get_head -x-
759
760 /*======================================================================*//**
761 @brief
762 Obtains the total size of the ring buffer, including both its utilized and
763 available portions.
764 @returns Total size of ring buffer
765 @see empty
766 @see get_available
767 @see get_utilized
768 @see set_size
769 @qualifier ADVANCED
770 *///=========================================================================
771 size_t get_size() noexcept {
772 std::lock_guard<std::mutex> guard(__mutex);
773 return __size;
774 } // -x- size_t get_size -x-
775
776 /*======================================================================*//**
777 @brief
778 Obtains the position of the tail in the ring buffer.
779 @returns Position of tail in ring buffer
780 @see get_available
781 @see get_head
782 @see get_utilized
783 @see set_head
784 @see set_tail
785 @qualifier ADVANCED
786 *///=========================================================================
787 size_t get_tail() noexcept {
788 std::lock_guard<std::mutex> guard(__mutex);
789 return __tail;
790 } // -x- size_t get_tail -x-
791
792 /*======================================================================*//**
793 @brief
794 Calculate the number of utilized entries in the ring buffer.
795 @returns Number of entries utilized
796 @see get_available
797 *///=========================================================================
798 size_t get_utilized() noexcept {
799 std::lock_guard<std::mutex> guard(__mutex);
800 return __utilized();
801 } // -x- size_t get_utilized -x-
802
803 /*======================================================================*//**
804 @brief
805 Indicate whether internal buffer's memory will be cleared by the destructor
806 before de-allocating it (default is @c true upon instantiation).
807
808 @note
809 This is an important security feature for when the ring buffer may have been
810 storing private data because it prevents the leaking of private data to other
811 process that coincidentally gain access to the same memory area (e.g., by way
812 of future calls to `malloc()`).
813 @returns Post-wipe policy (TRUE = clear / FALSE = don't clear)
814 @see set_wipe_policy
815 @qualifier ADVANCED
816 *///=========================================================================
817 bool get_wipe_policy() noexcept {
818 std::lock_guard<std::mutex> guard(__mutex);
819 return __wipe_policy;
820 } // -x- bool get_wipe_policy -x-
821
822 /*======================================================================*//**
823 @brief
824 Duplicate a single character from the utilized portion of the internal ring
825 buffer, and return it as an @c char primitive.
826 @exception std::overflow_error If the amount of data exceeds the amount of
827 data in the ring buffer
828 @returns Data from ring buffer
829 @see append
830 @see c_str
831 @see peek_vector
832 @see remove
833 @see remove_vector
834 *///=========================================================================
835 char peek() {
836 std::lock_guard<std::mutex> guard(__mutex);
837
838 // --------------------------------------------------------------------------
839 // Syntax checks.
840 // --------------------------------------------------------------------------
841 if (__utilized() == 0) throw std::overflow_error("Ring buffer is empty");
842
843 // --------------------------------------------------------------------------
844 // Duplicate the last byte of data from the ring buffer.
845 // --------------------------------------------------------------------------
846 return __buffer[__tail]; // Copy entry
847
848 } // -x- char peek -x-
849
850 /*======================================================================*//**
851 @brief
852 Duplicate the specified number of characters from the utilized portion of the
853 internal ring buffer, and return them as a @c char* array.
854 @post
855 The string returned will need to be `free()`'d after it's no longer needed.
856 @exception std::bad_alloc When @c malloc() fails to allocate memory
857 @exception std::overflow_error If the amount of data exceeds the amount of
858 data in the ring buffer
859 @returns Data from ring buffer
860 @see append
861 @see c_str
862 @see remove
863 @see remove_array
864 @see peek_string
865 @see peek_vector
866 *///=========================================================================
867 char* peek_array(
868 /// Amount of data to duplicate
869 size_t len,
870 /// Where to store the data@n
871 /// @c nullptr = allocate the needed memory using @c malloc()
872 char* data = nullptr) {
873
874 // --------------------------------------------------------------------------
875 // Internal variables.
876 // --------------------------------------------------------------------------
877 if (data == nullptr) data = (char*)malloc(len);
878 if (data == nullptr) throw std::bad_alloc();
879
880 // --------------------------------------------------------------------------
881 // Remove data from ring buffer.
882 // --------------------------------------------------------------------------
883 {
884 std::lock_guard<std::mutex> guard(__mutex);
885 size_t t = __tail;
886 for (int i = 0; i < len; i++) {
887 data[i] = __buffer[t]; // Copy entry
888 if (++t >= __size) t = 0; // Wrap-around
889 } // -x- for i -x-
890 }
891
892 return data;
893 } // -x- char* peek_array -x-
894
895 /*======================================================================*//**
896 @brief
897 Duplicate the specified number of characters from the utilized portion of the
898 internal ring buffer, and return it as an @c std::string object.
899 @exception std::length_error If data length is -2 or below
900 @exception std::overflow_error If the amount of data exceeds the amount of
901 data in the ring buffer
902 @returns Data from ring buffer
903 @see append
904 @see c_str
905 @see peek_vector
906 @see remove
907 @see remove_vector
908 *///=========================================================================
909 std::string peek_string(
910 /// Amount of data to duplicate@n
911 /// -1 = all data
912 int len = -1) {
913
914 // --------------------------------------------------------------------------
915 // Syntax checks.
916 // --------------------------------------------------------------------------
917 if (len < -1) throw std::length_error("Unrealistic data length of " + std::to_string(len));
918
919 // --------------------------------------------------------------------------
920 // Duplicate data from ring buffer. If no data is needed, then skip all the
921 // mutex locking overhead, etc., and return the empty string.
922 // --------------------------------------------------------------------------
923 std::string data;
924 if (len != 0) {
925 std::lock_guard<std::mutex> guard(__mutex);
926 if (len == -1) len = __utilized();
927 if (__utilized() < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds utilized space in ring buffer");
928 data.resize(len); // Pre-allocate needed memory
929 size_t t = __tail;
930 for (int i = 0; i < len; i++) {
931 data[i] = __buffer[t]; // Copy entry
932 if (++t >= __size) t = 0; // Wrap-around
933 } // -x- for i -x-
934 } // -x- if len -x-
935
936 return data;
937 } // -x- std::string peek_string -x-
938
939 /*======================================================================*//**
940 @brief
941 Duplicate the specified number of characters from the utilized portion of the
942 internal ring buffer, and return it as an @c std::string object.
943 @exception std::length_error If data length is -2 or below
944 @exception std::overflow_error If the amount of data exceeds the amount of
945 data in the ring buffer
946 @returns Data from ring buffer
947 @see append
948 @see c_str
949 @see peek
950 @see remove
951 @see remove_vector
952 *///=========================================================================
953 std::vector<char> peek_vector(
954 /// Amount of data to duplicate@n
955 /// -1 = all data
956 int len = -1) {
957
958 // --------------------------------------------------------------------------
959 // Syntax checks.
960 // --------------------------------------------------------------------------
961 if (len < -1) throw std::length_error("Unrealistic data length of " + std::to_string(len));
962
963 // --------------------------------------------------------------------------
964 // Duplicate data from ring buffer. If no data is needed, then skip all the
965 // mutex locking overhead, etc., and return the empty string.
966 // --------------------------------------------------------------------------
967 std::vector<char> data;
968 if (len != 0) {
969 std::lock_guard<std::mutex> guard(__mutex);
970 if (len == -1) len = __utilized();
971 if (__utilized() < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds utilized space in ring buffer");
972 data.resize(len); // Pre-allocate needed memory
973 size_t t = __tail;
974 for (int i = 0; i < len; i++) {
975 data[i] = __buffer[t]; // Copy entry
976 if (++t >= __size) t = 0; // Wrap-around
977 } // -x- for i -x-
978 } // -x- if len -x-
979
980 return data;
981 } // -x- std::vector<char> peek_vector -x-
982
983 /*======================================================================*//**
984 @brief
985 Remove one character from the utilized portion of the internal ring buffer,
986 and return it .
987 @exception std::overflow_error If the amount of data exceeds the amount of
988 data in the ring buffer
989 @returns Data from ring buffer
990 @see append
991 @see c_str
992 @see peek
993 @see remove_array
994 @see remove_string
995 @see remove_vector
996 *///=========================================================================
997 char remove() {
998
999 // --------------------------------------------------------------------------
1000 // Remove data from ring buffer. If no data is needed, then skip all the
1001 // mutex locking overhead, etc., and return the empty string.
1002 // --------------------------------------------------------------------------
1003 char ch;
1004 {
1005 std::lock_guard<std::mutex> guard(__mutex);
1006 if (__utilized() == 0) throw std::overflow_error("Ring buffer is empty");
1007 ch = __buffer[__tail]; // Copy entry
1008 if (++__tail >= __size) __tail = 0; // Wrap-around
1009 }
1010
1011 return ch;
1012 } // -x- char remove -x-
1013
1014 /*======================================================================*//**
1015 @brief
1016 Remove the specified number of characters from the utilized portion of the
1017 internal ring buffer, and return them as a @c char* array.
1018 @post
1019 The string returned will need to be `free()`'d after it's no longer needed.
1020 @exception std::bad_alloc When @c malloc() fails to allocate memory
1021 @exception std::overflow_error If the amount of data exceeds the amount of
1022 data in the ring buffer
1023 @returns Data from ring buffer
1024 @see append
1025 @see c_str
1026 @see peek
1027 @see remove
1028 @see remove_string
1029 @see remove_vector
1030 *///=========================================================================
1031 char* remove_array(
1032 /// Amount of data to remove
1033 size_t len,
1034 /// Where to store the data@n
1035 /// @c nullptr = allocate the needed memory using @c malloc()
1036 char* data = nullptr) {
1037
1038 // --------------------------------------------------------------------------
1039 // Internal variables.
1040 // --------------------------------------------------------------------------
1041 if (data == nullptr) data = (char*)malloc(len);
1042 if (data == nullptr) throw std::bad_alloc(); // Throw exception if data is still nullptr
1043
1044 // --------------------------------------------------------------------------
1045 // Remove data from ring buffer. If no data is needed, then skip all the
1046 // mutex locking overhead, etc., and return the empty string.
1047 // --------------------------------------------------------------------------
1048 {
1049 std::lock_guard<std::mutex> guard(__mutex);
1050 for (int i = 0; i < len; i++) {
1051 data[i] = __buffer[__tail]; // Copy entry
1052 if (++__tail >= __size) __tail = 0; // Wrap-around
1053 } // -x- for i -x-
1054 }
1055
1056 return data;
1057 } // -x- char* remove_array -x-
1058
1059 /*======================================================================*//**
1060 @brief
1061 Remove the specified number of characters from the utilized portion of the
1062 internal ring buffer, and return it as an @c std::string object.
1063 @exception std::length_error If data length is -2 or below
1064 @exception std::overflow_error If the amount of data exceeds the amount of
1065 data in the ring buffer
1066 @returns Data from ring buffer
1067 @see append
1068 @see c_str
1069 @see peek
1070 @see remove
1071 @see remove_string
1072 @see remove_vector
1073 *///=========================================================================
1074 std::string remove_string(
1075 /// Amount of data to remove@n
1076 /// -1 = all data
1077 int len = -1) {
1078
1079 // --------------------------------------------------------------------------
1080 // Syntax checks.
1081 // --------------------------------------------------------------------------
1082 if (len < -1) throw std::length_error("Unrealistic data length of " + std::to_string(len));
1083
1084 // --------------------------------------------------------------------------
1085 // Remove data from ring buffer. If no data is needed, then skip all the
1086 // mutex locking overhead, etc., and return the empty string.
1087 // --------------------------------------------------------------------------
1088 std::string data;
1089 if (len > 0) {
1090 std::lock_guard<std::mutex> guard(__mutex);
1091 if (len == -1) len = __utilized();
1092 if (__utilized() < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds utilized space in ring buffer");
1093 data.resize(len); // Pre-allocate needed memory
1094 for (int i = 0; i < len; i++) {
1095 data[i] = __buffer[__tail]; // Copy entry
1096 if (++__tail >= __size) __tail = 0; // Wrap-around
1097 } // -x- for i -x-
1098 } // -x- if len -x-
1099
1100 return data;
1101 } // -x- std::string remove_string -x-
1102
1103 /*======================================================================*//**
1104 @brief
1105 Remove the specified number of characters from the utilized portion of the
1106 internal ring buffer, and return it as an @c std::string object.
1107 @exception std::length_error If data length is -2 or below
1108 @exception std::overflow_error If the amount of data exceeds the amount of
1109 data in the ring buffer
1110 @returns Data from ring buffer
1111 @see append
1112 @see c_str
1113 @see peek
1114 @see remove
1115 @see remove_array
1116 @see remove_string
1117 *///=========================================================================
1118 std::vector<char> remove_vector(
1119 /// Amount of data to remove@n
1120 /// -1 = all data
1121 int len = -1) {
1122
1123 // --------------------------------------------------------------------------
1124 // Syntax checks.
1125 // --------------------------------------------------------------------------
1126 if (len < -1) throw std::length_error("Unrealistic data length of " + std::to_string(len));
1127
1128 // --------------------------------------------------------------------------
1129 // Remove data from ring buffer. If no data is needed, then skip all the
1130 // mutex locking overhead, etc., and return the empty string.
1131 // --------------------------------------------------------------------------
1132 std::vector<char> data;
1133 if (len > 0) {
1134 std::lock_guard<std::mutex> guard(__mutex);
1135 if (len == -1) len = __utilized();
1136 if (__utilized() < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds utilized space in ring buffer");
1137 data.resize(len); // Pre-allocate needed memory
1138 for (int i = 0; i < len; i++) {
1139 data[i] = __buffer[__tail]; // Copy entry
1140 if (++__tail >= __size) __tail = 0; // Wrap-around
1141 } // -x- for i -x-
1142 } // -x- if len -x-
1143
1144 return data;
1145 } // -x- std::vector<char> remove_vector -x-
1146
1147 /*======================================================================*//**
1148 @brief
1149 Alter the position of the internal head position, without changing any of the
1150 the pre-existing data in the underlying ring buffer.
1151 @exception std::overflow_error If the new position falls beyond the internal
1152 tail position (or, in other words, it exceeds
1153 the available free space in the ring buffer)
1154 @returns The same rring object so as to facilitate stacking
1155 @see get_available
1156 @see get_head
1157 @see get_tail
1158 @see get_utilized
1159 @see set_tail
1160 @qualifier ADVANCED
1161 *///=========================================================================
1162 rring* set_head(
1163 /// Position adjustment (may be positive or negative)
1164 int offset,
1165 /// Type of adjustment@n
1166 /// TRUE = absolute (default)@n
1167 /// FALSE = relative
1168 bool type = true) {
1169
1170 // --------------------------------------------------------------------------
1171 // Absolute position.
1172 // --------------------------------------------------------------------------
1173 if (type) {
1174 std::lock_guard<std::mutex> guard(__mutex);
1175 if (offset < 0) throw std::overflow_error("Head position adjustment of " + std::to_string(offset) + " cannot be negative");
1176 if (offset > __available()) throw std::overflow_error("Head position adjustment of " + std::to_string(offset) + " exceeds available space in ring buffer");
1177 __head = (__head + offset) % __size;
1178
1179 // --------------------------------------------------------------------------
1180 // Relative position.
1181 // --------------------------------------------------------------------------
1182 } else {
1183 if (offset > 0) { // Positive adjustment
1184 std::lock_guard<std::mutex> guard(__mutex);
1185 if (offset > __available()) throw std::overflow_error("Head position adjustment of " + std::to_string(offset) + " exceeds available space in ring buffer");
1186 __head = (__head + offset) % __size;
1187 } else if (offset < 0) { // Negative adjustment
1188 std::lock_guard<std::mutex> guard(__mutex);
1189 if (0 - offset > __utilized()) throw std::overflow_error("Head position adjustment of " + std::to_string(offset) + " exceeds utilized space in ring buffer");
1190 __head += offset;
1191 if (__head >= __size) __head += __size;
1192 } // -x- if offset -x-
1193 } // -x- if type -x-
1194
1195 return this;
1196 } // -x- rring* set_head -x-
1197
1198 /*======================================================================*//**
1199 @brief
1200 Configure the total size of the ring buffer. The new size cannot be smaller
1201 than the amount of data that's already present in the ring buffer (see the
1202 @ref get_utilized method to find out how much data is present).
1203 @exception std::overflow_error If the new size exceeds the amount of data
1204 that's already present in the ring buffer
1205 @returns The same rring object so as to facilitate stacking
1206 @see get_available
1207 @see get_size
1208 @see get_utilized
1209 *///=========================================================================
1210 rring* set_size(
1211 /// New size of ring buffer
1212 const size_t len) {
1213 {
1214 std::lock_guard<std::mutex> guard(__mutex);
1215 if (__utilized() < len) throw std::overflow_error("New size of ring buffer (len=" + std::to_string(len) + ") exceeds utilized amount");
1216 // TODO: Change (increase or decrease) total size of ring buffer, if possible,
1217 // which will entail moving data around if it wraps, and also throwing
1218 // an exception if the reduction is less than the utilized portion.
1219 __size = len;
1220 }
1221
1222 return this;
1223 } // -x- rring* set_size -x-
1224
1225 /*======================================================================*//**
1226 @brief
1227 Alter the position of the internal tail position, without changing any of the
1228 the pre-existing data in the underlying ring buffer.
1229 @exception std::overflow_error If the new position falls beyond the internal
1230 head position (or, in other words, it exceeds
1231 the available free space in the ring buffer)
1232 @returns The same rring object so as to facilitate stacking
1233 @see get_available
1234 @see get_head
1235 @see get_tail
1236 @see get_utilized
1237 @see set_head
1238 @qualifier ADVANCED
1239 *///=========================================================================
1240 rring* set_tail(
1241 /// Position adjustment (may be positive or negative)
1242 int offset,
1243 /// Type of adjustment@n
1244 /// TRUE = absolute (default)@n
1245 /// FALSE = relative
1246 bool type = true) {
1247
1248 // --------------------------------------------------------------------------
1249 // Absolute position.
1250 // --------------------------------------------------------------------------
1251 if (type) {
1252 std::lock_guard<std::mutex> guard(__mutex);
1253 if (offset < 0) throw std::overflow_error("Tail position adjustment of " + std::to_string(offset) + " cannot be negative");
1254 if (offset > __utilized()) throw std::overflow_error("Tail position adjustment of " + std::to_string(offset) + " exceeds utilized space in ring buffer");
1255 __tail = (__tail + offset) % __size;
1256
1257 // --------------------------------------------------------------------------
1258 // Relative position.
1259 // --------------------------------------------------------------------------
1260 } else {
1261 if (offset > 0) { // Positive position
1262 std::lock_guard<std::mutex> guard(__mutex);
1263 if (offset > __utilized()) throw std::overflow_error("Tail position adjustment of " + std::to_string(offset) + " exceeds utilized space in ring buffer");
1264 __tail = (__tail + offset) % __size;
1265 } else if (offset < 0) { // Negative adjustment
1266 std::lock_guard<std::mutex> guard(__mutex);
1267 if (0 - offset > __available()) throw std::overflow_error("Tail position adjustment of " + std::to_string(offset) + " exceeds available space in ring buffer");
1268 __tail += offset;
1269 if (__tail >= __size) __tail += __size;
1270 } // -x- if offset -x-
1271 } // -x- if type -x-
1272
1273 return this;
1274 } // -x- rring* set_tail -x-
1275
1276 /*======================================================================*//**
1277 @brief
1278 Specify whether the internal buffer's memory will be cleared by the
1279 destructor before de-allocating it (default is @c true upon instantiation).
1280
1281 @note
1282 This is an important security feature for when the ring buffer may have been
1283 storing private data because it prevents the leaking of private data to other
1284 process that coincidentally gain access to the same memory area (e.g., by way
1285 of future calls to `malloc()`).
1286 @returns The same rring object so as to facilitate stacking
1287 @see get_wipe_policy
1288 @qualifier ADVANCED
1289 *///=========================================================================
1290 rring* set_wipe_policy(
1291 /// TRUE = clear buffer memory in destructor@n
1292 /// FALSE = don't clear buffer memory in destructor
1293 const bool policy_flag) noexcept {
1294 {
1295 std::lock_guard<std::mutex> guard(__mutex);
1296 __wipe_policy = policy_flag;
1297 }
1298
1299 return this;
1300 } // -x- rring* set_wipe_policy -x-
1301
1302 /*======================================================================*//**
1303 @brief
1304 Convert the entire contents of this ring buffer to an ASCIIZ string in the
1305 form of a @c char* array.
1306 @post
1307 The string returned will need to be `free()`'d after it's no longer needed.
1308
1309 @code{.cpp}
1310 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1311 #include <randolf/rring>
1312
1313 int main(int argc, char *argv[]) {
1314 randolf::rring rb(1024);
1315 rb.append("This is an example.");
1316 char* c = rb;
1317 std::cout << "\"" << c << "\"" << std::endl;
1318 free(c);
1319 return EXIT_SUCCESS;
1320 } // -x- int main -x-
1321 @endcode
1322
1323 @exception std::bad_alloc When @c malloc() fails to allocate memory
1324 @returns Contents of this ring buffer as an ASCII string as a @c char* array.
1325 @see c_str
1326 *///=========================================================================
1327 operator char*() noexcept { return c_str(); }; // -x- operator char*() -x-
1328
1329 /*======================================================================*//**
1330 @brief
1331 Convert the entire contents of this ring buffer to an @c std::string object.
1332
1333 @code{.cpp}
1334 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1335 #include <string> // std::string
1336 #include <randolf/rring>
1337
1338 int main(int argc, char *argv[]) {
1339 randolf::rring rb(1024);
1340 rb.append("This is an example.");
1341 std::string s = rb;
1342 std::cout << "\"" << s << "\"" << std::endl;
1343 return EXIT_SUCCESS;
1344 } // -x- int main -x-
1345 @endcode
1346
1347 @returns Contents of this ring buffer as an std::string object
1348 @see peek_string
1349 *///=========================================================================
1350 operator std::string() noexcept { return peek_string(); }; // -x- operator std::string -x-
1351
1352 /*======================================================================*//**
1353 @brief
1354 Convert the entire contents of this ring buffer to an @c std::vector<char>
1355 object.
1356
1357 @code{.cpp}
1358 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1359 #include <string> // std::string
1360 #include <vector> // std::vector
1361 #include <randolf/rring>
1362
1363 int main(int argc, char *argv[]) {
1364 randolf::rring rb(1024);
1365 rb.append("This is an example.");
1366 std::vector<char> v = rb;
1367 std::cout << "\"" << std::string(v.begin(), v.end()) << "\"" << std::endl;
1368 return EXIT_SUCCESS;
1369 } // -x- int main -x-
1370 @endcode
1371
1372 @returns Contents of this ring buffer as an std::vector<char> object
1373 @see peek_vector
1374 *///=========================================================================
1375 operator std::vector<char>() noexcept { return peek_vector(); }; // -x- operator std::vector<char> -x-
1376
1377 /*======================================================================*//**
1378 @brief
1379 Support convenient streaming usage with std::cout, std::cerr, and friends.
1380
1381 @code{.cpp}
1382 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1383 #include <string> // std::string
1384 #include <randolf/rring>
1385
1386 int main(int argc, char *argv[]) {
1387 randolf::rring rb(1024);
1388 rb.append("This is an example.");
1389 std::cout << "\"" << rb << "\"" << std::endl;
1390 return EXIT_SUCCESS;
1391 } // -x- int main -x-
1392 @endcode
1393
1394 @returns Contents of this ring buffer as an ASCIIZ string (a.k.a., C-string)
1395 @see c_str
1396 *///=========================================================================
1397 friend std::ostream& operator<< (
1398 /// Output stream (provided automatically by std::cout and std::cerr)
1399 std::ostream& o,
1400 /// Object class (matched by compiler)
1401 rring& c) noexcept { return o << c.c_str(); }; // -x- std::ostream& operator<< -x-
1402
1403 /*======================================================================*//**
1404 @brief
1405 Array-style access to the utilized portion of the ring buffer's contents,
1406 which yields the same result as that of the @ref at() method.
1407
1408 The first element is at index 0.
1409 @throws std::out_of_range if the index is out-of-range
1410 @returns char
1411 @see at
1412 *///=========================================================================
1413 char operator[](
1414 /// Index of character to access (0 = first element; negative index values
1415 /// are calculated in reverse, starting with -1 as the final position)
1416 int index) {
1417 std::lock_guard<std::mutex> guard(__mutex);
1418
1419 // --------------------------------------------------------------------------
1420 // Internal variables.
1421 // --------------------------------------------------------------------------
1422 int MAX = __utilized();
1423
1424 // --------------------------------------------------------------------------
1425 // Syntax checks.
1426 // --------------------------------------------------------------------------
1427 if (index >= MAX || 0 - index > MAX) throw std::out_of_range("index (position=" + std::to_string(index) + ") falls outside of the utilized portion (length=" + std::to_string(MAX) + ") of the buffer");
1428
1429 // --------------------------------------------------------------------------
1430 // Calculate character's position.
1431 // --------------------------------------------------------------------------
1432 index = index >= 0 ? __tail + index : __head + index;
1433 if (index >= __size) index -= __size;
1434
1435 return __buffer[index];
1436 }; // -x- char operator[] -x-
1437
1438 }; // -x- class rring -x-
1439
1440}; // -x- namespace randolf -x-