9#include <randolf/rring_bam>
13 /*======================================================================*//**
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.
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).
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.
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.
42 Lower-case letters "rb" are regularly used in partial example code to
43 represent an instantiated rring object.
45 An ASCIIZ string is a C-string (char* array) that includes a terminating null
46 (0) character at the end.
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
53 @author Randolf Richardson
54 *///=========================================================================
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
65 /*======================================================================*//**
67 Instantiate an empty ring buffer (which can be re-sized or replaced later).
68 *///=========================================================================
72 }; // -x- constructor rring -x-
74 /*======================================================================*//**
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 *///=========================================================================
81 const size_t buffer_size) {
82 __buffer = (char*)malloc(buffer_size);
83 if (__buffer == nullptr) throw std::bad_alloc();
85 }; // -x- constructor rring -x-
87 /*======================================================================*//**
89 Free internal memory resources, after clearing buffer's memory (if the policy
90 indicates that it needs to be cleared).
91 *///=========================================================================
93 if (__buffer != nullptr) {
95 // --------------------------------------------------------------------------
96 // Overwrite buffer's memory with NULL if policy indicates it.
97 // --------------------------------------------------------------------------
98 if (__wipe_policy) while (__size > 0) __buffer[--__size] = '\0';
100 // --------------------------------------------------------------------------
101 // Free buffer's memory resource.
102 // --------------------------------------------------------------------------
105 } // -x- if __buffer -x-
106 }; // -x- destructor ~rring -x-
109 /*======================================================================*//**
111 Calculate the number of available entries in the ring buffer.
112 @returns Number of entries available
114 *///=========================================================================
115 size_t __available() noexcept {
116 return (__head < __tail) ? __tail - __head
117 : __size - (__head - __tail);
118 } // -x- size_t __available -x-
120 /*======================================================================*//**
122 Calculate the number of utilized entries in the ring buffer.
123 @returns Number of entries utilized
125 *///=========================================================================
126 size_t __utilized() noexcept {
127 return (__head >= __tail) ? __head - __tail
128 : __size - (__tail - __head);
129 } // -x- size_t __utilized -x-
132 /*======================================================================*//**
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
143 *///=========================================================================
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 // --------------------------------------------------------------------------
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
160 } // -x- rring* append -x-
162 /*======================================================================*//**
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
173 *///=========================================================================
177 /// Length of data (-1 = treat @c data as an ASCIIZ string)
180 // --------------------------------------------------------------------------
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));
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 // --------------------------------------------------------------------------
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
200 } // -x- rring* append -x-
202 /*======================================================================*//**
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
213 *///=========================================================================
216 const std::vector<char> data,
217 /// Length of data (-1 = treat @c data as an ASCIIZ string)
220 // --------------------------------------------------------------------------
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));
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 // --------------------------------------------------------------------------
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
240 } // -x- rring* append -x-
242 /*======================================================================*//**
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
253 *///=========================================================================
256 const std::vector<unsigned char> data,
257 /// Length of data (-1 = treat @c data as an ASCIIZ string)
260 // --------------------------------------------------------------------------
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));
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 // --------------------------------------------------------------------------
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
280 } // -x- rring* append -x-
282 /*======================================================================*//**
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.
287 The first element is at index 0.
288 @throws std::out_of_range if the index is out-of-range
291 *///=========================================================================
293 /// Index of character to access (0 = first element; negative index values
294 /// are calculated in reverse, starting with -1 as the final position)
296 std::lock_guard<std::mutex> guard(__mutex);
298 // --------------------------------------------------------------------------
299 // Internal variables.
300 // --------------------------------------------------------------------------
301 int MAX = __utilized();
303 // --------------------------------------------------------------------------
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");
308 // --------------------------------------------------------------------------
309 // Calculate character's position.
310 // --------------------------------------------------------------------------
311 index = index >= 0 ? __tail + index : __head + index;
312 if (index >= __size) index -= __size;
314 return __buffer[index];
317 /*======================================================================*//**
319 Return the contents of the ring buffer as an ASCIIZ string.
321 The string returned will need to be `free()`'d after it's no longer needed.
324 #include <iostream> // std::cout, std::cerr, std::endl, etc.
325 #include <randolf/rring>
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;
334 } // -x- int main -x-
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
348 *///=========================================================================
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 {
355 // --------------------------------------------------------------------------
356 // Internal variables.
357 // --------------------------------------------------------------------------
360 // --------------------------------------------------------------------------
361 // Generate ASCIIZ string.
362 // --------------------------------------------------------------------------
364 std::lock_guard<std::mutex> guard(__mutex);
366 if (data == nullptr) data = (char*)malloc(len + 1);
367 if (data == nullptr) throw std::bad_alloc();
369 for (int i = 0; i < len; i++) {
370 data[i] = __buffer[t]; // Copy entry
371 if (++t >= __size) t = 0; // Wrap-around
374 data[len] = '\0'; // Not "len - 1" because we already allocated "len + 1"
377 } // -x- char* c_str -x-
379 /*======================================================================*//**
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
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)
394 *///=========================================================================
395 char* data() noexcept {
396 std::lock_guard<std::mutex> guard(__mutex);
398 } // -x- char* data -x-
400 /*======================================================================*//**
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.
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.
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.).
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
419 @exception std::bad_alloc When @c malloc() fails to allocate memory
420 @returns Pointer to @ref rring_bam structure
425 *///=========================================================================
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);
433 // --------------------------------------------------------------------------
434 // Internal variables.
435 // --------------------------------------------------------------------------
436 if (bam == nullptr) {
437 bam = (rring_bam*)malloc(__size);
438 if (bam == nullptr) throw std::bad_alloc();
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;
450 bam->block1_size = __size - __tail;
451 bam->block2 = __size - __tail == 0 ? nullptr : __buffer;
452 bam->block2_size = __head;
453 } // -x- if __head -x-
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;
464 bam->avail1_size = __size - __head;
465 bam->avail2 = __tail == 0 ? nullptr : __buffer;
466 bam->avail2_size = __tail;
467 } // -x- if __head -x-
470 } // -x- rring_bam* data_bam -x-
472 /*======================================================================*//**
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
479 @returns The same rring object so as to facilitate stacking
485 *///=========================================================================
486 rring* data_bam_adjust(
487 /// New internal head position
489 /// New internal tail position
492 // --------------------------------------------------------------------------
493 // Set new head and tail positions.
494 // --------------------------------------------------------------------------
496 std::lock_guard<std::mutex> guard(__mutex);
502 } // -x- rring* data_bam_adjust -x-
504 /*======================================================================*//**
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.
512 *///=========================================================================
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
524 const bool full = true) {
526 std::lock_guard<std::mutex> guard(__mutex);
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];
538 case 0: // Buffer is empty
542 } // -x- switch __tail -x-
544 // --------------------------------------------------------------------------
545 // Internal variables.
546 // --------------------------------------------------------------------------
547 char carry; // Used in swapping elements
548 char last; // Only used if existing
549 size_t len = __utilized();
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-
560 // --------------------------------------------------------------------------
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;
571 __tail = 0; // Reposition the tail
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-
583 } // -x- rring* defragment -x-
58656781234 Simple swap (fastest speed)
5935671234 Remove-and-hold last (fastest speed)
5981234567 Re-instate last
6005671234 1.5 passes (slowest speed)
6095671234 Carry (medium speed)
616 /*======================================================================*//**
618 Discard data from the ring buffer.
619 @returns Number of bytes of data that was discarded
621 *///=========================================================================
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
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();
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
641 return len; // Return quantity that was actually discarded
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
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-
654 return discarded - len; // Return quantity that was actually discarded
656 } // -x- int discard -x-
658 /*======================================================================*//**
660 Indicates whether the ring buffer is empty.
661 @returns TRUE = empty@nFALSE = not empty
665 *///=========================================================================
666 bool empty() noexcept {
667 std::lock_guard<std::mutex> guard(__mutex);
668 return __head == __tail;
669 } // -x- bool empty -x-
671 /*======================================================================*//**
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
685 *///=========================================================================
686 std::string get_ascii_map(
687 /// Desired size of ASCII map@n
688 /// -1 = Use size of ring buffer
690 /// Character(s) representing utilized entries
691 const std::string u = "|",
692 /// Character(s) representing available entries
693 const std::string a = ".") noexcept {
695 // --------------------------------------------------------------------------
696 // Internal variables, which will be copied .
697 // --------------------------------------------------------------------------
702 std::lock_guard<std::mutex> guard(__mutex);
709 // --------------------------------------------------------------------------
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));
716 // --------------------------------------------------------------------------
718 // --------------------------------------------------------------------------
720 head = head * (len / size);
721 tail = tail * (len / size);
723 head = head / (size / len);
724 tail = tail / (size / len);
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
731 } // -x- std::string get_ascii_map -x-
733 /*======================================================================*//**
735 Calculate the number of available entries in the ring buffer.
736 @returns Number of entries available
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-
744 /*======================================================================*//**
746 Obtains the position of the head in the ring buffer.
747 @returns Position of head in ring buffer
754 *///=========================================================================
755 size_t get_head() noexcept {
756 std::lock_guard<std::mutex> guard(__mutex);
758 } // -x- size_t get_head -x-
760 /*======================================================================*//**
762 Obtains the total size of the ring buffer, including both its utilized and
764 @returns Total size of ring buffer
770 *///=========================================================================
771 size_t get_size() noexcept {
772 std::lock_guard<std::mutex> guard(__mutex);
774 } // -x- size_t get_size -x-
776 /*======================================================================*//**
778 Obtains the position of the tail in the ring buffer.
779 @returns Position of tail in ring buffer
786 *///=========================================================================
787 size_t get_tail() noexcept {
788 std::lock_guard<std::mutex> guard(__mutex);
790 } // -x- size_t get_tail -x-
792 /*======================================================================*//**
794 Calculate the number of utilized entries in the ring buffer.
795 @returns Number of entries utilized
797 *///=========================================================================
798 size_t get_utilized() noexcept {
799 std::lock_guard<std::mutex> guard(__mutex);
801 } // -x- size_t get_utilized -x-
803 /*======================================================================*//**
805 Indicate whether internal buffer's memory will be cleared by the destructor
806 before de-allocating it (default is @c true upon instantiation).
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)
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-
822 /*======================================================================*//**
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
834 *///=========================================================================
836 std::lock_guard<std::mutex> guard(__mutex);
838 // --------------------------------------------------------------------------
840 // --------------------------------------------------------------------------
841 if (__utilized() == 0) throw std::overflow_error("Ring buffer is empty");
843 // --------------------------------------------------------------------------
844 // Duplicate the last byte of data from the ring buffer.
845 // --------------------------------------------------------------------------
846 return __buffer[__tail]; // Copy entry
848 } // -x- char peek -x-
850 /*======================================================================*//**
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.
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
866 *///=========================================================================
868 /// Amount of data to duplicate
870 /// Where to store the data@n
871 /// @c nullptr = allocate the needed memory using @c malloc()
872 char* data = nullptr) {
874 // --------------------------------------------------------------------------
875 // Internal variables.
876 // --------------------------------------------------------------------------
877 if (data == nullptr) data = (char*)malloc(len);
878 if (data == nullptr) throw std::bad_alloc();
880 // --------------------------------------------------------------------------
881 // Remove data from ring buffer.
882 // --------------------------------------------------------------------------
884 std::lock_guard<std::mutex> guard(__mutex);
886 for (int i = 0; i < len; i++) {
887 data[i] = __buffer[t]; // Copy entry
888 if (++t >= __size) t = 0; // Wrap-around
893 } // -x- char* peek_array -x-
895 /*======================================================================*//**
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
908 *///=========================================================================
909 std::string peek_string(
910 /// Amount of data to duplicate@n
914 // --------------------------------------------------------------------------
916 // --------------------------------------------------------------------------
917 if (len < -1) throw std::length_error("Unrealistic data length of " + std::to_string(len));
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 // --------------------------------------------------------------------------
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
930 for (int i = 0; i < len; i++) {
931 data[i] = __buffer[t]; // Copy entry
932 if (++t >= __size) t = 0; // Wrap-around
937 } // -x- std::string peek_string -x-
939 /*======================================================================*//**
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
952 *///=========================================================================
953 std::vector<char> peek_vector(
954 /// Amount of data to duplicate@n
958 // --------------------------------------------------------------------------
960 // --------------------------------------------------------------------------
961 if (len < -1) throw std::length_error("Unrealistic data length of " + std::to_string(len));
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;
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
974 for (int i = 0; i < len; i++) {
975 data[i] = __buffer[t]; // Copy entry
976 if (++t >= __size) t = 0; // Wrap-around
981 } // -x- std::vector<char> peek_vector -x-
983 /*======================================================================*//**
985 Remove one character from the utilized portion of the internal ring buffer,
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
996 *///=========================================================================
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 // --------------------------------------------------------------------------
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
1012 } // -x- char remove -x-
1014 /*======================================================================*//**
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.
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
1030 *///=========================================================================
1032 /// Amount of data to remove
1034 /// Where to store the data@n
1035 /// @c nullptr = allocate the needed memory using @c malloc()
1036 char* data = nullptr) {
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
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 // --------------------------------------------------------------------------
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
1057 } // -x- char* remove_array -x-
1059 /*======================================================================*//**
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
1073 *///=========================================================================
1074 std::string remove_string(
1075 /// Amount of data to remove@n
1079 // --------------------------------------------------------------------------
1081 // --------------------------------------------------------------------------
1082 if (len < -1) throw std::length_error("Unrealistic data length of " + std::to_string(len));
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 // --------------------------------------------------------------------------
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
1101 } // -x- std::string remove_string -x-
1103 /*======================================================================*//**
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
1117 *///=========================================================================
1118 std::vector<char> remove_vector(
1119 /// Amount of data to remove@n
1123 // --------------------------------------------------------------------------
1125 // --------------------------------------------------------------------------
1126 if (len < -1) throw std::length_error("Unrealistic data length of " + std::to_string(len));
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;
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
1145 } // -x- std::vector<char> remove_vector -x-
1147 /*======================================================================*//**
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
1161 *///=========================================================================
1163 /// Position adjustment (may be positive or negative)
1165 /// Type of adjustment@n
1166 /// TRUE = absolute (default)@n
1167 /// FALSE = relative
1170 // --------------------------------------------------------------------------
1171 // Absolute position.
1172 // --------------------------------------------------------------------------
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;
1179 // --------------------------------------------------------------------------
1180 // Relative position.
1181 // --------------------------------------------------------------------------
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");
1191 if (__head >= __size) __head += __size;
1192 } // -x- if offset -x-
1193 } // -x- if type -x-
1196 } // -x- rring* set_head -x-
1198 /*======================================================================*//**
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
1209 *///=========================================================================
1211 /// New size of ring buffer
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.
1223 } // -x- rring* set_size -x-
1225 /*======================================================================*//**
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
1239 *///=========================================================================
1241 /// Position adjustment (may be positive or negative)
1243 /// Type of adjustment@n
1244 /// TRUE = absolute (default)@n
1245 /// FALSE = relative
1248 // --------------------------------------------------------------------------
1249 // Absolute position.
1250 // --------------------------------------------------------------------------
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;
1257 // --------------------------------------------------------------------------
1258 // Relative position.
1259 // --------------------------------------------------------------------------
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");
1269 if (__tail >= __size) __tail += __size;
1270 } // -x- if offset -x-
1271 } // -x- if type -x-
1274 } // -x- rring* set_tail -x-
1276 /*======================================================================*//**
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).
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
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 {
1295 std::lock_guard<std::mutex> guard(__mutex);
1296 __wipe_policy = policy_flag;
1300 } // -x- rring* set_wipe_policy -x-
1302 /*======================================================================*//**
1304 Convert the entire contents of this ring buffer to an ASCIIZ string in the
1305 form of a @c char* array.
1307 The string returned will need to be `free()`'d after it's no longer needed.
1310 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1311 #include <randolf/rring>
1313 int main(int argc, char *argv[]) {
1314 randolf::rring rb(1024);
1315 rb.append("This is an example.");
1317 std::cout << "\"" << c << "\"" << std::endl;
1319 return EXIT_SUCCESS;
1320 } // -x- int main -x-
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.
1326 *///=========================================================================
1327 operator char*() noexcept { return c_str(); }; // -x- operator char*() -x-
1329 /*======================================================================*//**
1331 Convert the entire contents of this ring buffer to an @c std::string object.
1334 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1335 #include <string> // std::string
1336 #include <randolf/rring>
1338 int main(int argc, char *argv[]) {
1339 randolf::rring rb(1024);
1340 rb.append("This is an example.");
1342 std::cout << "\"" << s << "\"" << std::endl;
1343 return EXIT_SUCCESS;
1344 } // -x- int main -x-
1347 @returns Contents of this ring buffer as an std::string object
1349 *///=========================================================================
1350 operator std::string() noexcept { return peek_string(); }; // -x- operator std::string -x-
1352 /*======================================================================*//**
1354 Convert the entire contents of this ring buffer to an @c std::vector<char>
1358 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1359 #include <string> // std::string
1360 #include <vector> // std::vector
1361 #include <randolf/rring>
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-
1372 @returns Contents of this ring buffer as an std::vector<char> object
1374 *///=========================================================================
1375 operator std::vector<char>() noexcept { return peek_vector(); }; // -x- operator std::vector<char> -x-
1377 /*======================================================================*//**
1379 Support convenient streaming usage with std::cout, std::cerr, and friends.
1382 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1383 #include <string> // std::string
1384 #include <randolf/rring>
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-
1394 @returns Contents of this ring buffer as an ASCIIZ string (a.k.a., C-string)
1396 *///=========================================================================
1397 friend std::ostream& operator<< (
1398 /// Output stream (provided automatically by std::cout and std::cerr)
1400 /// Object class (matched by compiler)
1401 rring& c) noexcept { return o << c.c_str(); }; // -x- std::ostream& operator<< -x-
1403 /*======================================================================*//**
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.
1408 The first element is at index 0.
1409 @throws std::out_of_range if the index is out-of-range
1412 *///=========================================================================
1414 /// Index of character to access (0 = first element; negative index values
1415 /// are calculated in reverse, starting with -1 as the final position)
1417 std::lock_guard<std::mutex> guard(__mutex);
1419 // --------------------------------------------------------------------------
1420 // Internal variables.
1421 // --------------------------------------------------------------------------
1422 int MAX = __utilized();
1424 // --------------------------------------------------------------------------
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");
1429 // --------------------------------------------------------------------------
1430 // Calculate character's position.
1431 // --------------------------------------------------------------------------
1432 index = index >= 0 ? __tail + index : __head + index;
1433 if (index >= __size) index -= __size;
1435 return __buffer[index];
1436 }; // -x- char operator[] -x-
1438 }; // -x- class rring -x-
1440}; // -x- namespace randolf -x-