3#include <algorithm> // std::min and std::max
10#include <randolf/rline>
11#include <randolf/rring_bam>
15 /*======================================================================*//**
17 Ring buffer implementation for 8-bit bytes that's threadsafe and resizeable,
18 and the methods are designed to be efficient in various ways, including by
19 supporting the transfer of multiple bytes in variable quantities.
21 Data is stored internally as a @c char* array, but a few methods are provided
22 to also support easy interoperability with @c std::vector objects since there
23 are many scenarios where this can be helpful (and from the perspective of
24 optimization, this eliminates the cumbersome need to utilize conversions
25 between @c std::string and @c std::vector objects since, internally, we use
26 direct access to/from @c std::vector objects where needed).
29 Occasionally, ring buffers (a.k.a., circular buffers) are useful in specific
30 situations because they provide better performance compared to other types of
31 buffers, which either results in frequent allocations and deallocations of
32 memory for transient-but-ordered portions of data or tracking sections of
33 transient-but-ordered portions of data. A ring buffer relies on a single
34 allocation of memory, along with a few variables for tracking top and bottom
35 positions plus a few other internals to ensure smooth and accurate usage.
37 Expanding and reducing ring buffer memory automatically (which is disabled by
38 default) is a feature that's particularly useful for server daemons that
39 process small amounts of data most of the time. For example, an SMTP, IMAP4,
40 POP3, MEOW, FTP, HTTP, FINGER, or WHOIS server that is rqeuired to minimize
41 per-connection memory requirements can have a line buffer that begins with 80
42 characters, and be configured to increase to millions on-demand that will
43 automatically shrink back down to the minimal 80 character buffer size, hence
44 most of the time the buffer will only utilize 80 characters, with occasional
45 longer lines that can be rejected efficiently or handled in some other way by
46 the daemon without the complexity of memory management and, most importantly,
47 without having to pre-allocate larger amounts of memory "just in case" a few
48 connections start sending long lines (such as corrupt data, buffer overflow
49 attempts that could be part of fuzzing tests or hacking efforts, etc.).
52 Lower-case letters "rb" are regularly used in partial example code to
53 represent an instantiated rring object.
55 An ASCIIZ string is a C-string (char* array) that includes a terminating null
56 (0) character at the end.
59 - 2024-Nov-25 v1.00 Initial version
60 - 2024-Dec-03 v1.00 Added @ref rring_bam structure integration
61 - 2024-Dec-03 v1.00 Added @ref data_bam and related methods
62 - 2024-Dec-23 v1.00 Added @ref defragment and @ref reverse methods
63 - 2024-Dec-23 v1.00 Renamed @c peek_ methods to @c peek_to_ and @c remove_
64 methods to @c remove_to, so that the intention of these
65 methods is more easily understood
66 - 2024-Dec-23 v1.00 Added internal methods to support ring buffer resizing
67 - 2024-Dec-24 v1.00 Added constructor that includes automatic expansion
69 - 2024-Dec-25 v1.00 Implemented automatic buffer size expansion policies
70 - 2024-Dec-27 v1.00 Automatic buffer size expansion, reduction, and block
71 resizing implementations are complete, and tested
72 - 2024-Jan-07 v1.00 Added @ref copy_to_rline method
73 - 2024-Jan-09 v1.00 Added optional @c len parameter to various constructor,
74 assign, and append methods for greater flexibility
75 - 2024-Jan-12 v1.00 Changed std::length_error to std::invalid_argument in
76 response to length being specified as a negative value
77 so this can more easily be handled with seperate code
79 @author Randolf Richardson
80 *///=========================================================================
83 char* __buffer; // Pointer to buffer
84 size_t __size; // Size of buffer // TODO: Add a base_size to ease later calculations with xp
85 size_t __head = 0; // Head (beginning)
86 size_t __tail = 0; // Tail (end)
87 std::mutex __mutex; // Used to add support for multiple threads
89 // --------------------------------------------------------------------------
90 // Dynamic expansion variables.
91 // --------------------------------------------------------------------------
92 uint __xp_block_size; // Expansion increment/decrement block size (default == __size)
93 uint __xp_max = 0; // Maximum possible number of expansion blocks
94 uint __xp_play = 2; // Number of available blocks must be present before attempting reduction
95 uint __xp_used = 0; // Number of expansion increments used
97 // --------------------------------------------------------------------------
99 // --------------------------------------------------------------------------
100 bool __wipe_policy = true; // Destructor memory wipe-out policy
103 /*======================================================================*//**
105 Instantiate an empty ring buffer (without automatic resizing-expansions).
106 @exception std::bad_alloc If @c malloc() fails to allocate memory
107 *///=========================================================================
110 const size_t buffer_size) {
111 __buffer = (char*)malloc(buffer_size);
112 if (__buffer == nullptr) throw std::bad_alloc(); // Throw exception if __buffer wasn't allocated by malloc
113 __size = buffer_size;
114 __xp_block_size = buffer_size;
115 }; // -x- constructor rring -x-
117 /*======================================================================*//**
119 Instantiate an empty ring buffer (with automatic resizing-expansion policies
120 set so the internal buffer will be re-sized to adapt to unexpected allocation
121 needs behind-the-scenes automatically).
122 @exception std::bad_alloc If @c malloc() fails to allocate memory
123 *///=========================================================================
126 const size_t buffer_size,
127 /// Maximum possible number of expansion blocks@n
128 /// 0 = no expansions / effectively disabling expansion (default)
130 /// Minimum number of available blocks before a reduction can occur
131 const uint xp_play = 2,
132 /// Size of expansion blocks @n
133 /// 0 = use @c buffer_size (default) @n
134 const uint xp_block_size = 0
136 __buffer = (char*)malloc(buffer_size);
137 if (__buffer == nullptr) throw std::bad_alloc(); // Throw exception if __buffer wasn't allocated by malloc
138 __size = buffer_size;
140 // --------------------------------------------------------------------------
141 // Dynamic expansion variables.
142 // --------------------------------------------------------------------------
145 __xp_block_size = xp_block_size == 0 ? __size : xp_block_size;
147 }; // -x- constructor rring -x-
149 /*======================================================================*//**
151 Free internal memory resources, after clearing buffer's memory (if the policy
152 indicates that it needs to be cleared).
153 *///=========================================================================
155 if (__buffer != nullptr) {
157 // --------------------------------------------------------------------------
158 // Overwrite buffer's memory with NULL if policy indicates it.
159 // --------------------------------------------------------------------------
160 if (__wipe_policy) while (__size > 0) __buffer[--__size] = '\0';
162 // --------------------------------------------------------------------------
163 // Free buffer's memory resource.
164 // --------------------------------------------------------------------------
167 } // -x- if __buffer -x-
168 }; // -x- destructor ~rring -x-
171 // @returns Quantity of entries added (if positive) or removed (if negative)
172 // if ring buffer memory was increased/decreased respectively@n
173 // 0 = there was no change in ring buffer memory
174 /*======================================================================*//**
176 Calculate the number of available entries in the ring buffer, and expand or
177 contract the size of the buffer memory if needed quantity calls for it.
178 @exception std::overflow_error If the quantity of needed entries exceeds the
179 available free space in the ring buffer
180 @returns If `need > 0`, number of entries available, same as __available() @n
181 If `need <= 0`, number of entries removed (0 == none removed)
182 *///=========================================================================
184 /// Number of additional entries to attempt to make available@n
185 /// 0 = attempt to reduce ring buffer memory (with no intention to expand it)
189 // --------------------------------------------------------------------------
190 // Easy checks first.
191 // --------------------------------------------------------------------------
192 if (__xp_max == 0) return 0; // Expandable memory is effectively disabled, so nothing can change
194 // --------------------------------------------------------------------------
195 // Internal variables.
196 // --------------------------------------------------------------------------
197 size_t available = (__head < __tail) ? __tail - __head
198 : __size - (__head - __tail);
200 // --------------------------------------------------------------------------
201 // Reduce memory (as per policies) if there's no need to increase memory.
202 // --------------------------------------------------------------------------
205 // --------------------------------------------------------------------------
206 // Internal variables.
207 // --------------------------------------------------------------------------
208 uint play = __xp_play == 0 ? __xp_block_size : __xp_play * __xp_block_size;
210//std::cout << "play=" << play << " available=" << available << " __xp_used=" << __xp_used << " __xp_play=" << __xp_play << std::endl;
211 // --------------------------------------------------------------------------
212 // If there isn't enough "play," then we don't need to reduce buffer memory.
214 // The reason we don't throw an exception is that reducing ring buffer memory
215 // here is a passive background function, so it's not actually an error when
216 // the conditions for reduction are not satisfied.
217 // --------------------------------------------------------------------------
218 if (available < play /* && __xp_used < __xp_play */) return 0; // No need to reduce buffer memory (because __xp_play is less than what's available)
219 if (available < __xp_used * __xp_block_size) return 0; // No need to reduce buffer memory (because some/all of it is still in use)
221 // --------------------------------------------------------------------------
222 // Allocate less memory and move buffer into it using __defragment (which
223 // takes care of re-allocating the new memory).
225 // Note: Utilized memory is calculated using "__size - available" and then
226 // we add "play" to maintain the minimal amount of available memory
227 // --------------------------------------------------------------------------
228 uint old_size = __size;
229 uint r_blocks = std::min((uint)(available / __xp_block_size), __xp_used);
230 uint new_size = __size - r_blocks * __xp_block_size;
231 __defragment(true, new_size);
232 __xp_used -= r_blocks;
234//std::cout << "old_size=" << old_size << " new_size=" << new_size << std::endl;
235 // --------------------------------------------------------------------------
236 // Return quantity of entries removed since the original need specified 0.
237 // --------------------------------------------------------------------------
238 return old_size - new_size;
242 // --------------------------------------------------------------------------
243 // If there's already enough memory, then simply return the quantity that's
244 // already available.
245 // --------------------------------------------------------------------------
246 if (need <= available) return available; // Return already-calculated quantity of available entries
248 // --------------------------------------------------------------------------
249 // Calculate number of blocks needed, then check that it's possible to
250 // accomodate what's needed or throw an exception if there isn't enough.
251 // --------------------------------------------------------------------------
252 uint xp_blocks_needed = need / __xp_block_size + (need % __xp_block_size != 0); // Total number of blocks needed, mathematically rounded up
253//std::cout << "__size=" << __size << " xp_blocks_needed=" << xp_blocks_needed << " need=" << need << " __xp_max=" << __xp_max << " __xp_used=" << __xp_used << std::endl;
254 if (xp_blocks_needed > __xp_max - __xp_used) throw std::overflow_error("Amount of needed data (quantity=" + std::to_string(need) + ") exceeds expansion limits");
256 // --------------------------------------------------------------------------
257 // Allocate new memory and move buffer into it using __defragment (which
258 // takes care of re-allocating the new memory).
259 // --------------------------------------------------------------------------
260 __defragment(true, __size + __xp_block_size * xp_blocks_needed);
262 // --------------------------------------------------------------------------
263 // If __defragment()'s memory allocation was successful, then it means that
264 // the resizing was successful, and so now it's okay to update any important
266 // --------------------------------------------------------------------------
267 __xp_used = xp_blocks_needed; // Not += (as of 2024-Dec-27)
268//std::cout << "__size=" << __size << " xp_blocks_needed=" << xp_blocks_needed << " need=" << need << " __xp_max=" << __xp_max << " __xp_used=" << __xp_used << std::endl;
270 return __available(); // Return re-calculated quantity of available entries because __defragment probably changed them
272 } // -x- int __adjust_xp -x-
274 /*======================================================================*//**
276 Calculate the number of available entries in the ring buffer.
277 @returns Number of entries available
279 *///=========================================================================
280 size_t __available() noexcept {
281 return (__head < __tail) ? __tail - __head
282 : __size - (__head - __tail);
283 } // -x- size_t __available -x-
285 /*======================================================================*//**
287 Calculate the number of utilized entries in the ring buffer.
288 @returns Number of entries utilized
290 *///=========================================================================
291 size_t __utilized() noexcept {
292 return (__head >= __tail) ? __head - __tail
293 : __size - (__tail - __head);
294 } // -x- size_t __utilized -x-
297 /*======================================================================*//**
299 Add one character to the unused portion of the internal ring buffer.
300 @exception std::overflow_error If the amount of data exceeds the available
301 free space in the ring buffer
302 @returns The same rring object so as to facilitate stacking
305 *///=========================================================================
310 // --------------------------------------------------------------------------
311 // Add data to ring buffer. If no data is needed, then skip all the mutex
312 // locking overhead, etc., and return the empty string.
313 // --------------------------------------------------------------------------
314 { std::lock_guard<std::mutex> guard(__mutex);
315 if (__adjust_xp(1) == 0) throw std::overflow_error("Ring buffer is full");
316 if (__head >= __size) __head = 0; // Wrap-around
317 __buffer[__head] = data; // Copy entry
318 __head++; // Advance to next entry
319 } // -x- mutex guard -x-
322 } // -x- rring* append -x-
324 /*======================================================================*//**
326 Add any number of characters that fit within the unused portion of the
327 internal ring buffer.
328 @exception std::invalid_argument If data length is -2 or below
329 @exception std::overflow_error If the amount of data exceeds the available
330 free space in the ring buffer
331 @returns The same rring object so as to facilitate stacking
335 *///=========================================================================
339 /// Length of data (-1 = treat @c data as an ASCIIZ string)
342 // --------------------------------------------------------------------------
344 // --------------------------------------------------------------------------
345 if (len == -1) len = std::strlen(data);
346 else if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
348 // --------------------------------------------------------------------------
349 // Add data to ring buffer. If no data is needed, then skip all the mutex
350 // locking overhead, etc., and return the empty string.
351 // --------------------------------------------------------------------------
353 std::lock_guard<std::mutex> guard(__mutex);
354 if (__adjust_xp(len) < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds available free space in ring buffer");
355 for (int i = 0; i < len; i++) {
356 if (__head >= __size) __head = 0; // Wrap-around
357 __buffer[__head] = data[i]; // Copy entry
358 __head++; // Advance to next entry
363 } // -x- rring* append -x-
365 /*======================================================================*//**
367 Add any number of characters that fit within the unused portion of the
368 internal ring buffer.
369 @exception std::invalid_argument If data length is -2 or below
370 @exception std::length_error If data length is too long
371 @exception std::overflow_error If the amount of data exceeds the available
372 free space in the ring buffer
373 @returns The same rring object so as to facilitate stacking
376 @see remove_to_string
377 *///=========================================================================
380 const std::string data,
381 /// Length of data (-1 = rely on data.length())
384 // --------------------------------------------------------------------------
386 // --------------------------------------------------------------------------
387 if (len == -1 ) len = data.length();
388 else if (len < -1 ) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
389 else if (len > data.length()) throw std::length_error("Unrealistic data length of " + std::to_string(len));
391 // --------------------------------------------------------------------------
392 // Add data to ring buffer. If no data is needed, then skip all the mutex
393 // locking overhead, etc., and return the empty string.
394 // --------------------------------------------------------------------------
396 std::lock_guard<std::mutex> guard(__mutex);
397 if (__adjust_xp(len) < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds available free space in ring buffer");
398 for (int i = 0; i < len; i++) {
399 if (__head >= __size) __head = 0; // Wrap-around
400 __buffer[__head] = data[i]; // Copy entry
401 __head++; // Advance to next entry
406 } // -x- rring* append -x-
408 /*======================================================================*//**
410 Add any number of characters that fit within the unused portion of the
411 internal ring buffer.
412 @exception std::invalid_argument If data length is -2 or below
413 @exception std::overflow_error If the amount of data exceeds the available
414 free space in the ring buffer
415 @returns The same rring object so as to facilitate stacking
418 @see remove_to_vector
419 *///=========================================================================
422 const std::vector<char> data,
423 /// Length of data (-1 = treat @c data as an ASCIIZ string)
426 // --------------------------------------------------------------------------
428 // --------------------------------------------------------------------------
429 if (len == -1) len = data.size();
430 else if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
432 // --------------------------------------------------------------------------
433 // Add data to ring buffer. If no data is needed, then skip all the mutex
434 // locking overhead, etc., and return the empty string.
435 // --------------------------------------------------------------------------
437 std::lock_guard<std::mutex> guard(__mutex);
438 if (__adjust_xp(len) < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds available free space in ring buffer");
439 for (int i = 0; i < len; i++) {
440 if (__head >= __size) __head = 0; // Wrap-around
441 __buffer[__head] = data[i]; // Copy entry
442 __head++; // Advance to next entry
447 } // -x- rring* append -x-
449 /*======================================================================*//**
451 Add any number of characters that fit within the unused portion of the
452 internal ring buffer.
453 @exception std::invalid_argument If data length is -2 or below
454 @exception std::overflow_error If the amount of data exceeds the available
455 free space in the ring buffer
456 @returns The same rring object so as to facilitate stacking
459 @see remove_to_vector
460 *///=========================================================================
463 const std::vector<unsigned char> data,
464 /// Length of data (-1 = treat @c data as an ASCIIZ string)
467 // --------------------------------------------------------------------------
469 // --------------------------------------------------------------------------
470 if (len == -1) len = data.size();
471 else if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
473 // --------------------------------------------------------------------------
474 // Add data to ring buffer. If no data is needed, then skip all the mutex
475 // locking overhead, etc., and return the empty string.
476 // --------------------------------------------------------------------------
478 std::lock_guard<std::mutex> guard(__mutex);
479 if (__adjust_xp(len) < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds available free space in ring buffer");
480 for (int i = 0; i < len; i++) {
481 if (__head >= __size) __head = 0; // Wrap-around
482 __buffer[__head] = data[i]; // Copy entry
483 __head++; // Advance to next entry
488 } // -x- rring* append -x-
490 /*======================================================================*//**
492 Array-style access to the utilized portion of the ring buffer's contents,
493 which yields the same result as that of the @ref at() method.
495 The first element is at index 0.
496 @throws std::out_of_range if the index is out-of-range
499 *///=========================================================================
501 /// Index of character to access (0 = first element; negative index values
502 /// are calculated in reverse, starting with -1 as the final position)
504 std::lock_guard<std::mutex> guard(__mutex);
506 // --------------------------------------------------------------------------
507 // Internal variables.
508 // --------------------------------------------------------------------------
509 int MAX = __utilized();
511 // --------------------------------------------------------------------------
513 // --------------------------------------------------------------------------
514 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");
516 // --------------------------------------------------------------------------
517 // Calculate character's position.
518 // --------------------------------------------------------------------------
519 index = index >= 0 ? __tail + index : __head + index;
520 if (index >= __size) index -= __size;
522 return __buffer[index];
525 /*======================================================================*//**
527 Return the contents of the ring buffer as an ASCIIZ string.
529 The string returned will need to be `free()`'d after it's no longer needed.
532 #include <iostream> // std::cout, std::cerr, std::endl, etc.
533 #include <randolf/rring>
535 int main(int argc, char *argv[]) {
536 randolf::rring rb(1024);
537 rb.append("This is an example.");
538 char* c = rb.c_str();
539 std::cout << "\"" << c << "\"" << std::endl;
542 } // -x- int main -x-
546 When using the @ref data parameter in a multi-threaded scenario, the amount
547 of the memory needed should be set to the maximum size of this ring buffer to
548 prevent unintended under-allocation resulting from a simultaneous increase
549 in utilized ring buffer memory (caused by @ref set_size).
550 @exception std::bad_alloc If @c malloc() fails to allocate memory
551 @returns ASCIIZ string
552 @see append(const char*, int)
553 @see append(const std::string, int)
556 *///=========================================================================
558 /// Where to store the ASCIIZ string (including terminating zero, so it is
559 /// imperative that `utilized() + 1` bytes be pre-allocated)@n
560 /// @c nullptr = allocate the needed memory using @c malloc()
561 char* data = nullptr) noexcept {
563 // --------------------------------------------------------------------------
564 // Internal variables.
565 // --------------------------------------------------------------------------
568 // --------------------------------------------------------------------------
569 // Generate ASCIIZ string.
570 // --------------------------------------------------------------------------
571 { std::lock_guard<std::mutex> guard(__mutex);
573 if (data == nullptr) {
574 data = (char*)malloc(len + 1);
575 if (data == nullptr) throw std::bad_alloc(); // Throw exception if data wasn't allocated by malloc
576 } // -x- if !data -x-
578 for (int i = 0; i < len; i++) {
579 if (t >= __size) t = 0; // Wrap-around
580 data[i] = __buffer[t]; // Copy entry
581 t++; // Advance to next entry
583 } // -x- mutex guard -x-
584 data[len] = '\0'; // Not "len - 1" because we already allocated "len + 1"
587 } // -x- char* c_str -x-
589 /*======================================================================*//**
591 Copy a line of text into a randolf::rline object. If an EoL sequence is not
592 detected, the data will still fill the randolf::rline object.
594 If providing a pre-existing randolf::rline object, data will be appended to
595 any existing data that's already present.
596 @throws std::invalid_argument If @c len is less than -1
597 @returns Data from ring buffer
598 @see append(const std::string, int)
604 *///=========================================================================
605 randolf::rline* copy_to_rline(
606 /// Maximum quantity of data to duplicate@n
609 /// Pointer to an already-instantiated @ref randolf::rline object to use@n
610 /// nullptr = instantiate a new @ref randolf::rline object (this will need to
611 /// be `delete`'d when it's no longer needed)
612 randolf::rline* data = nullptr) {
614 // --------------------------------------------------------------------------
616 // --------------------------------------------------------------------------
617 if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
618 if (data == nullptr) data = new randolf::rline();
620 // --------------------------------------------------------------------------
621 // Duplicate data from ring buffer, quickly (in one or two moves).
622 // --------------------------------------------------------------------------
623 { std::lock_guard<std::mutex> guard(__mutex);
625 case -1: // Copy everything
626 if (__head >= __tail) { // Not wrapped
627 data->append(__buffer + __tail, __utilized());
629 int n = __size - __tail;
630 data->append(__buffer + __tail, std::min(len, n));
631 if (n < len) data->append(__buffer, len - n);
632 } // -x- if not_wrapped -x-
633 case 0: // Copy nothing at all
635 default: // Copy specified quantity
636 int utilized = __utilized();
637 if (utilized < len) len = utilized; // throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds utilized space in ring buffer");
638 if (__head >= __tail) { // Not wrapped
639 data->append(__buffer + __tail, std::min(len, utilized));
641 int n = __size - __tail;
642 data->append(__buffer + __tail, std::min(len, n));
643 if (n < len) data->append(__buffer, len - n);
644 } // -x- if not_wrapped -x-
645 } // -x- switch len -x-
646 } // -x- mutex guard -x-
649 } // -x- randolf::rline* copy_to_rline -x-
651 /*======================================================================*//**
653 Returns a pointer to the underlying ring buffer's array. If the "defragment"
654 flag is cleared then the beginning of the array may not be the same as where
655 the data actually begins (and ends) within the buffer (see the @c warning
659 This is the live data, which should not be accessed or modified directly; use
660 the @ref data_bam() and @ref data_bam_adjust() methods instead when there is
661 a need to access or modify the backing array directly.
662 @exception std::bad_alloc If @c malloc() fails to allocate memory (this can
663 only occur when the "defragment" flag is set)
664 @returns Pointer to buffer (not null terminated)
672 *///=========================================================================
674 /// Whether to defragment the data first@n
675 /// @c TRUE = fuly defragment so that data begins at position 0 (default) @n
676 /// @c FALSE = don't defragment the data (this is an advanced feature that
677 /// will require knowing how to use the @ref data_bam method, and
678 /// also the @ref data_bam method if adding or removing data)
679 bool defragment_flag = true) noexcept {
680 std::lock_guard<std::mutex> guard(__mutex);
681 if (defragment_flag) defragment(true);
683 } // -x- char* data -x-
685 /*======================================================================*//**
687 Returns the lengths and addresses of available memory. There may be only one
688 block of memory, or two, depending on whether the available memory straddles
689 the boundary of the ring buffer.
691 This is the nearest equivalent of the @ref data() methods provided in classes
692 such as @c std::string and @c std::vector, but with the nuance of supporting
693 the underpinnings of this ring buffer wherein the utilized portions may be
694 stored at both the end and beginning of the buffer memory.
696 The intention of this method is to provide an opportunity to directly update
697 the contents of the ring buffer's data section in an efficient manner (e.g.,
698 such as by reading file or socket data directly into memory without having to
699 perform separate data copying operations, etc.).
701 Upon updating a portion of the available-data block, the @c head will also
702 need to be updated accordingly, for which the @ref data_bam_adjust method is
704 @exception std::bad_alloc If @c malloc() fails to allocate memory
705 @returns Pointer to @ref rring_bam structure
715 *///=========================================================================
717 /// Pointer to @ref rring_bam structure (that's already been allocated)@n
718 /// @c nullptr (default) = allocate memory for this structure with @c malloc
719 /// (which will need to be `free`'d when it's no longer needed)
720 rring_bam* bam = nullptr) noexcept {
721 std::lock_guard<std::mutex> guard(__mutex);
723 // --------------------------------------------------------------------------
724 // Internal variables.
725 // --------------------------------------------------------------------------
726 if (bam == nullptr) {
727 bam = (rring_bam*)malloc(__size);
728 if (bam == nullptr) throw std::bad_alloc(); // Throw exception if bam wasn't allocated by malloc
731 // --------------------------------------------------------------------------
732 // Configure utilized data block pointer(s) and length(s).
733 // --------------------------------------------------------------------------
734 bam->block1 = __buffer + __tail;
735 if (__head >= __tail) { // Not wrapped
736 bam->block1_size = __head - __tail;
737 bam->block2 = nullptr;
738 bam->block2_size = 0;
740 bam->block1_size = __size - __tail;
741 bam->block2 = __size - __tail == 0 ? nullptr : __buffer;
742 bam->block2_size = __head;
743 } // -x- if __head -x-
745 // --------------------------------------------------------------------------
746 // Configure available data block pointer(s) and length(s).
747 // --------------------------------------------------------------------------
748 bam->avail1 = __buffer + __head;
749 if (__head < __tail) { // Not wrapped
750 bam->avail1_size = __tail - __head;
751 bam->avail2 = nullptr;
752 bam->avail2_size = 0;
754 bam->avail1_size = __size - __head;
755 bam->avail2 = __tail == 0 ? nullptr : __buffer;
756 bam->avail2_size = __tail;
757 } // -x- if __head -x-
760 } // -x- rring_bam* data_bam -x-
762 /*======================================================================*//**
764 Arbitrarily change the head and tail positions in the ring buffer. This is
765 intended for use in conjunction with appending data directly to the ring
766 buffer after the @ref data_bam method.
767 @exception std::overflow_error If either adjustment would result in causing
769 @returns The same rring object so as to facilitate stacking
775 *///=========================================================================
776 rring* data_bam_adjust(
777 /// New internal head position
779 /// New internal tail position
782 // --------------------------------------------------------------------------
783 // Set new head and tail positions.
784 // --------------------------------------------------------------------------
785 { std::lock_guard<std::mutex> guard(__mutex);
788 } // -x- mutex guard -x-
791 } // -x- rring* data_bam_adjust -x-
794 /*======================================================================*//**
796 Internal method used primarily by the defragment() and resize() methods.
798 A full defragmentation always results in the tail changing to position 0.
800 TO DO: Return to code in _archive/2024-Dec-22/ which is nearly finished, and
801 attempt to get the algorithm working for the last few bytes so that
802 the memory allocation approach we use now won't be needed (and also
803 therefore eliminates the wipe_policy implementation step).
804 @exception std::length_error If the new @c buffer_size is less than the total
805 quantity of utilized entries (which would result
806 in the truncation of data)
807 @exception std::bad_alloc If @c malloc() fails to allocate memory
810 *///=========================================================================
812 /// Whether to defrag when the utilized block isn't straddling the internal
813 /// memory buffer boundary@n
814 /// TRUE = always fully defragment (default) @n
815 /// FALSE = only defragment if utilized block straddles memory boundary
817 /// Total size of new memory buffer to be allocated (used in resizing so that
818 /// we only allocate the new buffer once {here} rather than twice {here, and
819 /// in any of the methods that @ref resize the internal buffer})
820 const size_t buffer_size) {
822 // --------------------------------------------------------------------------
823 // Internal variables.
824 // --------------------------------------------------------------------------
825 const size_t utilized = __utilized();
827 // --------------------------------------------------------------------------
828 // Perform quick checks if size isn't increasing.
829 // --------------------------------------------------------------------------
830 if (buffer_size == __size) {
832 // --------------------------------------------------------------------------
833 // Already fully optimized, so don't even start.
834 // --------------------------------------------------------------------------
835 if (__tail == 0) return;
837 // --------------------------------------------------------------------------
838 // If not wrapped around ring buffer (and not resizing), optimization is a
839 // fast-and-easy matter of shifting all the elements without re-allocating
840 // the ring buffer memory.
841 // --------------------------------------------------------------------------
842 if (__tail <= __head) {
843 if (!full) return; // No work to be done
844 for (size_t i = 0; i < utilized; i++) // Move data
845 __buffer[i] = __buffer[__tail++];
849 } // -x- if __tail -x-
851 // --------------------------------------------------------------------------
852 // Size reduction sanity check.
853 // --------------------------------------------------------------------------
854 } else if (buffer_size < utilized) {
856 // --------------------------------------------------------------------------
857 // Prevent truncation by throwing a length_error exception.
858 // --------------------------------------------------------------------------
859 throw std::length_error("Unrealistic buffer size reduction to " + std::to_string(buffer_size));
861 } // -x- if buffer_size -x-
863 // --------------------------------------------------------------------------
864 // Allocate new internal buffer.
865 // --------------------------------------------------------------------------
866 void* data = (char*)malloc(buffer_size); // Allocate new buffer to the same as as the current one
867 if (data == nullptr) throw std::bad_alloc(); // Throw exception if data wasn't allocated by malloc
869 // --------------------------------------------------------------------------
870 // Copy data to new buffer that is the same size as the current one.
871 // --------------------------------------------------------------------------
872 for (int i = 0; i < utilized; i++) {
873 if (__tail >= __size) __tail = 0; // Wrap-around
874 ((char*)data)[i] = __buffer[__tail]; // Copy entry
875 __tail++; // Advance to next entry
878 // --------------------------------------------------------------------------
879 // De-allocate old internal buffer, and wipe old data before de-allocation if
880 // the policy warrants it.
881 // --------------------------------------------------------------------------
883 for (int i = 0; i < __size; i++)
887 // --------------------------------------------------------------------------
888 // Set new buffer pointer and internal variables.
889 // --------------------------------------------------------------------------
890 __buffer = (char*)data; // Update pointer to internal buffer
893 __size = buffer_size;
896 } // -x- void __defragment -x-
899 /*======================================================================*//**
901 Reorganizes the utilized memory in the ring buffer memory by moving the tail
902 to the beginning. Defragmentation is completed in the most efficient manner,
903 depending on what needs to be done. If data wraps around the buffer memory,
904 then new memory will be allocated and data will be moved to it (old memory
905 will also be cleared if the wipe policy is in effect).
907 A full defragmentation always results in the tail changing to position 0.
908 @exception std::length_error If the new @c buffer_size is less than the total
909 quantity of utilized entries (which would result
910 in the truncation of data)
911 @exception std::bad_alloc If @c malloc() fails to allocate memory
912 @returns The same rring object so as to facilitate stacking
918 *///=========================================================================
920 /// Whether to defrag when the utilized block isn't straddling the internal
921 /// memory buffer boundary@n
922 /// TRUE = always fully defragment (default) @n
923 /// FALSE = only defragment if utilized block straddles memory boundary (this
924 /// favours reducing processor overhead if all that's wanted is to
925 /// have one block of memory to contend with in the BAM, hence it's
926 /// also what the @ref set_size() method uses for better performance
927 /// overall because resizing most likely occurs when the need for CPU
928 /// cycles are more critical, so we're doing our part by reducing our
930 const bool full = true) {
931 { std::lock_guard<std::mutex> guard(__mutex);
932 __defragment(full, __size);
933 } // -x- mutex guard -x-
935 } // -x- rring* defragment -x-
937 /*======================================================================*//**
939 Discard data from the ring buffer.
940 @throws std::invalid_argument If @c len is less than -1
941 @returns Number of entries that were discarded
943 *///=========================================================================
945 /// Number of bytes to discard@n
946 /// -1 = all utilized data that remains will be discarded (default)
947 int len = -1) noexcept {
948 if (len == 0) return 0; // Do nothing
950 { std::lock_guard<std::mutex> guard(__mutex);
951 if (len == -1) len = __size;
952 else if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
953 discarded = __utilized();
955 // --------------------------------------------------------------------------
956 // Discard everything because len is greater than or equal to the amount of
957 // data in the ring buffer.
958 // --------------------------------------------------------------------------
959 if (len >= discarded) { // Discard everything if len is too large
962 __adjust_xp(); // Ring buffer memory allocation maintenance
963 return len; // Return quantity that was actually discarded
966 // --------------------------------------------------------------------------
967 // Adjust __tail according to amount of data to discard. Calculating this is
968 // enormously faster than looping through the buffer.
969 // --------------------------------------------------------------------------
970 if (__head >= __tail || __size - __tail < len) { // Not wrapping, so adjustment is easy
972 } else { // Wrapping, so change tail to discard size minus what's discarded from the end
973 __tail = len - (__size - __tail);
974 } // -x- if __head -x-
976 // --------------------------------------------------------------------------
977 // Ring buffer memory allocation maintenance.
978 // --------------------------------------------------------------------------
981 } // -x- mutex guard -x-
983 return discarded - len; // Return quantity that was actually discarded
984 } // -x- int discard -x-
986 /*======================================================================*//**
988 Indicates whether the ring buffer is empty.
989 @returns TRUE = empty@n
995 *///=========================================================================
996 bool empty() noexcept {
997 std::lock_guard<std::mutex> guard(__mutex);
998 return __head == __tail;
999 } // -x- bool empty -x-
1001 /*======================================================================*//**
1003 Obtains a one-line ASCII art depiction of a map of the ring buffer's memory
1004 usage, wherein the utilized and available portions are represented by the `|`
1005 (pipe) and `.` (period) characters, respectively. These characters are
1006 configurable and may each contain more than one character, which is
1007 particularly useful for text-mode console output in a typical Linux shell in
1008 which @c ANSI or @c AVATAR codes may be used to present these characters or
1009 sequences in different colours, although there may be other uses for this
1011 @exception std::invalid_argument If length is -2 or below
1012 @returns One-line ASCII depiction of ring buffer's memory map
1017 *///=========================================================================
1018 std::string get_ascii_map(
1019 /// Desired size of ASCII map@n
1020 /// -1 = Use size of entire ring buffer
1022 /// Character(s) representing utilized entries
1023 const std::string u = "|",
1024 /// Character(s) representing available entries
1025 const std::string a = ".") noexcept {
1027 // --------------------------------------------------------------------------
1028 // Internal variables, which will be copied .
1029 // --------------------------------------------------------------------------
1033 { std::lock_guard<std::mutex> guard(__mutex);
1037 } // -x- mutex guard -x-
1040 // --------------------------------------------------------------------------
1042 // --------------------------------------------------------------------------
1043 if (len == -1) len = size;
1044 if (len == 0) return map;
1045 if (len <= -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
1047 // --------------------------------------------------------------------------
1049 // --------------------------------------------------------------------------
1051 head = head * (len / size);
1052 tail = tail * (len / size);
1054 head = head / (size / len);
1055 tail = tail / (size / len);
1057 map.reserve(len); // Pre-allocate the exact amount of underlying memory needed
1058 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
1059 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
1062 } // -x- std::string get_ascii_map -x-
1064 /*======================================================================*//**
1066 Calculate the number of available entries in the ring buffer.
1067 @returns Number of entries available
1070 @see get_xp_blocks_available
1071 *///=========================================================================
1072 size_t get_available() noexcept {
1073 std::lock_guard<std::mutex> guard(__mutex);
1074 return __available();
1075 } // -x- size_t get_available -x-
1077 /*======================================================================*//**
1079 Obtains the base size of the ring buffer, regardless of how much is utilized
1080 or available, and without any of the expansion policy limits.
1081 @returns Maximum potential size of ring buffer
1089 *///=========================================================================
1090 size_t get_base_size() noexcept {
1091 std::lock_guard<std::mutex> guard(__mutex);
1092 return __size - (__xp_used * __xp_block_size);
1093 } // -x- size_t get_base_size -x-
1095 /*======================================================================*//**
1097 Obtains the position of the head in the ring buffer.
1098 @returns Position of head in ring buffer
1106 *///=========================================================================
1107 size_t get_head() noexcept {
1108 std::lock_guard<std::mutex> guard(__mutex);
1110 } // -x- size_t get_head -x-
1112 /*======================================================================*//**
1114 Obtains the potential total size of the ring buffer, including both its
1115 utilized and available portions, including the maximum limit of what the
1116 expansion policies can extend the internal ring buffer's size to.
1117 @returns Maximum potential size of ring buffer
1126 *///=========================================================================
1127 size_t get_max_size() noexcept {
1128 std::lock_guard<std::mutex> guard(__mutex);
1129 return __size + ((__xp_max - __xp_used) * __xp_block_size);
1130 } // -x- size_t get_max_size -x-
1132 /*======================================================================*//**
1134 Obtains the total size of the current ring buffer, including both its
1135 utilized and available portions.
1136 @returns Total current size of ring buffer
1144 *///=========================================================================
1145 size_t get_size() noexcept {
1146 std::lock_guard<std::mutex> guard(__mutex);
1148 } // -x- size_t get_size -x-
1150 /*======================================================================*//**
1152 Obtains the position of the tail in the ring buffer.
1153 @returns Position of tail in ring buffer
1161 *///=========================================================================
1162 size_t get_tail() noexcept {
1163 std::lock_guard<std::mutex> guard(__mutex);
1165 } // -x- size_t get_tail -x-
1167 /*======================================================================*//**
1169 Calculate the number of utilized entries in the ring buffer.
1170 @returns Number of entries utilized
1173 @see get_xp_blocks_utilized
1174 *///=========================================================================
1175 size_t get_utilized() noexcept {
1176 std::lock_guard<std::mutex> guard(__mutex);
1177 return __utilized();
1178 } // -x- size_t get_utilized -x-
1180 /*======================================================================*//**
1182 Indicate whether internal buffer's memory will be cleared by the destructor
1183 before de-allocating it (default is @c true upon instantiation).
1186 This is an important security feature for when the ring buffer may have been
1187 storing private data because it prevents the leaking of private data to other
1188 process that coincidentally gain access to the same memory area (e.g., by way
1189 of future calls to `malloc()`).
1190 @returns Post-wipe policy (TRUE = clear / FALSE = don't clear)
1191 @see set_wipe_policy
1193 *///=========================================================================
1194 bool get_wipe_policy() noexcept {
1195 std::lock_guard<std::mutex> guard(__mutex);
1196 return __wipe_policy;
1197 } // -x- bool get_wipe_policy -x-
1199 /*======================================================================*//**
1201 Obtain block size in expansion policy.
1202 @returns Expansion policy's block size
1205 @see set_xp_block_size
1207 *///=========================================================================
1208 size_t get_xp_block_size() noexcept {
1209 std::lock_guard<std::mutex> guard(__mutex);
1210 return __xp_block_size;
1211 } // -x- size_t get_xp_block_size -x-
1213 /*======================================================================*//**
1215 Obtain quantity of blocks available for use in buffer ring memory expansions.
1216 @returns Quantity of expansion blocks available
1218 @see get_xp_block_size
1219 @see get_xp_blocks_utilized
1223 *///=========================================================================
1224 size_t get_xp_blocks_available() noexcept {
1225 std::lock_guard<std::mutex> guard(__mutex);
1226 return __xp_max - __xp_used;
1227 } // -x- size_t get_xp_blocks_available -x-
1229 /*======================================================================*//**
1231 Obtain quantity of blocks utilized utilized for buffer ring memory expansion.
1232 @returns Quantity of expansion blocks utilized
1234 @see get_xp_block_size
1235 @see get_xp_blocks_available
1239 *///=========================================================================
1240 size_t get_xp_blocks_utilized() noexcept {
1241 std::lock_guard<std::mutex> guard(__mutex);
1243 } // -x- size_t get_xp_blocks_utilized -x-
1245 /*======================================================================*//**
1247 Obtain maximum block quantity in expansion policy.
1248 @returns Expansion policy's maximum block quantity
1249 @see get_xp_block_size
1250 @see get_xp_blocks_available
1251 @see get_xp_blocks_utilized
1255 *///=========================================================================
1256 size_t get_xp_max() noexcept {
1257 std::lock_guard<std::mutex> guard(__mutex);
1259 } // -x- size_t get_xp_max -x-
1261 /*======================================================================*//**
1263 Obtain minimum quantity of blocks available before automatic expansion block
1265 @returns Expansion policy's play setting
1266 @see get_xp_block_size
1267 @see get_xp_blocks_available
1268 @see get_xp_blocks_utilized
1272 *///=========================================================================
1273 size_t get_xp_play() noexcept {
1274 std::lock_guard<std::mutex> guard(__mutex);
1276 } // -x- size_t get_xp_play -x-
1278 /*======================================================================*//**
1280 Duplicate a single character from the utilized portion of the internal ring
1281 buffer, and return it as an @c char primitive.
1282 @exception std::overflow_error If the amount of data exceeds the amount of
1283 data in the ring buffer
1284 @returns Data from ring buffer
1285 @see append(const char)
1291 *///=========================================================================
1293 std::lock_guard<std::mutex> guard(__mutex);
1295 // --------------------------------------------------------------------------
1297 // --------------------------------------------------------------------------
1298 if (__utilized() == 0) throw std::overflow_error("Ring buffer is empty");
1300 // --------------------------------------------------------------------------
1301 // Duplicate the last byte of data from the ring buffer.
1302 // --------------------------------------------------------------------------
1303 return __buffer[__tail]; // Copy entry
1305 } // -x- char peek -x-
1307 /*======================================================================*//**
1309 Duplicate the specified number of characters from the utilized portion of the
1310 internal ring buffer, and return them as a @c char* array.
1312 The string returned will need to be `free()`'d after it's no longer needed.
1313 @exception std::bad_alloc If @c malloc() fails to allocate memory
1314 @exception std::overflow_error If the amount of data exceeds the amount of
1315 data in the ring buffer
1316 @returns Data from ring buffer
1317 @see append(const char*, int)
1322 @see remove_to_array
1323 *///=========================================================================
1324 char* peek_to_array(
1325 /// Amount of data to duplicate
1327 /// Where to store the data@n
1328 /// @c nullptr = allocate the needed memory using @c malloc()
1329 char* data = nullptr) {
1331 // --------------------------------------------------------------------------
1332 // Internal variables.
1333 // --------------------------------------------------------------------------
1334 if (data == nullptr) {
1335 data = (char*)malloc(len);
1336 if (data == nullptr) throw std::bad_alloc(); // Throw exception if data wasn't allocated by malloc
1337 } // -x- if !data -x-
1339 // --------------------------------------------------------------------------
1340 // Remove data from ring buffer.
1341 // --------------------------------------------------------------------------
1342 { std::lock_guard<std::mutex> guard(__mutex);
1344 for (int i = 0; i < len; i++) {
1345 if (t >= __size) t = 0; // Wrap-around
1346 data[i] = __buffer[t]; // Copy entry
1347 t++; // Advance to next entry
1349 } // -x- mutex guard -x-
1352 } // -x- char* peek_to_array -x-
1354 /*======================================================================*//**
1356 Duplicate the specified number of characters from the utilized portion of the
1357 internal ring buffer, and return it as an @c std::string object.
1358 @exception std::invalid_argument If data length is -2 or below
1359 @exception std::overflow_error If the amount of data exceeds the amount of
1360 data in the ring buffer
1361 @returns Data from ring buffer
1362 @see append(const std::string, int)
1367 @see remove_to_string
1368 *///=========================================================================
1369 std::string peek_to_string(
1370 /// Amount of data to duplicate@n
1373 /// The previously instantiated std::string object to use (the default is to
1374 /// instantiate a new instance of std::string).
1375 std::string data = std::string()) {
1377 // --------------------------------------------------------------------------
1379 // --------------------------------------------------------------------------
1380 if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
1382 // --------------------------------------------------------------------------
1383 // Duplicate data from ring buffer. If no data is needed, then skip all the
1384 // mutex locking overhead, etc., and return the empty string.
1385 // --------------------------------------------------------------------------
1387 std::lock_guard<std::mutex> guard(__mutex);
1388 if (len == -1) len = __utilized();
1389 if (__utilized() < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds utilized space in ring buffer");
1390 data.resize(len); // Pre-allocate needed memory
1392 for (int i = 0; i < len; i++) {
1393 if (t >= __size) t = 0; // Wrap-around
1394 data[i] = __buffer[t]; // Copy entry
1395 t++; // Advance to next entry
1400 } // -x- std::string peek_to_string -x-
1402 /*======================================================================*//**
1404 Duplicate the specified number of characters from the utilized portion of the
1405 internal ring buffer, and return it as an @c std::string object.
1406 @exception std::invalid_argument If data length is -2 or below
1407 @exception std::overflow_error If the amount of data exceeds the amount of
1408 data in the ring buffer
1409 @returns Data from ring buffer
1410 @see append(const std::vector<char>, int)
1411 @see append(const std::vector<unsigned char>, int)
1416 @see remove_to_vector
1417 *///=========================================================================
1418 std::vector<char> peek_to_vector(
1419 /// Amount of data to duplicate@n
1422 /// The previously instantiated std::string object to use (the default is to
1423 /// instantiate a new instance of std::string).
1424 std::vector<char> data = std::vector<char>()) {
1426 // --------------------------------------------------------------------------
1428 // --------------------------------------------------------------------------
1429 if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
1431 // --------------------------------------------------------------------------
1432 // Duplicate data from ring buffer. If no data is needed, then skip all the
1433 // mutex locking overhead, etc., and return the empty string.
1434 // --------------------------------------------------------------------------
1436 std::lock_guard<std::mutex> guard(__mutex);
1437 if (len == -1) len = __utilized();
1438 if (__utilized() < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds utilized space in ring buffer");
1439 data.resize(len); // Pre-allocate needed memory
1441 for (int i = 0; i < len; i++) {
1442 if (t >= __size) t = 0; // Wrap-around
1443 data[i] = __buffer[t]; // Copy entry
1444 t++; // Advance to next entry
1449 } // -x- std::vector<char> peek_to_vector -x-
1451 /*======================================================================*//**
1453 Remove one character from the utilized portion of the internal ring buffer,
1455 @exception std::overflow_error If the amount of data exceeds the amount of
1456 data in the ring buffer
1457 @returns Data from ring buffer
1458 @see append(const char)
1461 @see remove_to_array
1462 @see remove_to_string
1463 @see remove_to_vector
1464 *///=========================================================================
1467 // --------------------------------------------------------------------------
1468 // Remove data from ring buffer. If no data is needed, then skip all the
1469 // mutex locking overhead, etc., and return the empty string.
1470 // --------------------------------------------------------------------------
1472 { std::lock_guard<std::mutex> guard(__mutex);
1473 if (__utilized() == 0) throw std::overflow_error("Ring buffer is empty");
1474 if (__tail >= __size) __tail = 0; // Wrap-around
1475 ch = __buffer[__tail]; // Copy entry
1476 __tail++; // Advance to next entry
1477 } // -x- mutex guard -x-
1479 // --------------------------------------------------------------------------
1480 // Ring buffer memory allocation maintenance.
1481 // --------------------------------------------------------------------------
1485 } // -x- char remove -x-
1487 /*======================================================================*//**
1489 Remove the specified number of characters from the utilized portion of the
1490 internal ring buffer, and return them as a @c char* array.
1492 The string returned will need to be `free()`'d after it's no longer needed.
1493 @exception std::bad_alloc If @c malloc() fails to allocate memory
1494 @exception std::overflow_error If the amount of data exceeds the amount of
1495 data in the ring buffer
1496 @returns Data from ring buffer
1497 @see append(const char*, int)
1501 @see remove_to_string
1502 @see remove_to_vector
1503 *///=========================================================================
1504 char* remove_to_array(
1505 /// Maximum quantity of data to move@n
1507 /// Where to store the data@n
1508 /// @c nullptr = allocate the needed memory using @c malloc()
1509 char* data = nullptr) {
1511 // --------------------------------------------------------------------------
1512 // Internal variables.
1513 // --------------------------------------------------------------------------
1514 if (data == nullptr) {
1515 data = (char*)malloc(len);
1516 if (data == nullptr) throw std::bad_alloc(); // Throw exception if data wasn't allocated by malloc
1517 } // -x- if !data -x-
1519 // --------------------------------------------------------------------------
1520 // Remove data from ring buffer. If no data is needed, then skip all the
1521 // mutex locking overhead, etc., and return the empty string.
1522 // --------------------------------------------------------------------------
1523 { std::lock_guard<std::mutex> guard(__mutex);
1524 for (int i = 0; i < len; i++) {
1525 if (__tail >= __size) __tail = 0; // Wrap-around
1526 data[i] = __buffer[__tail]; // Copy entry
1527 __tail++; // Advance to next entry
1529 } // -x- mutex guard -x-
1531 // --------------------------------------------------------------------------
1532 // Ring buffer memory allocation maintenance.
1533 // --------------------------------------------------------------------------
1537 } // -x- char* remove_to_array -x-
1539 /*======================================================================*//**
1541 Remove the specified number of characters from the utilized portion of the
1542 internal ring buffer, and return it as an @c std::string object.
1543 @exception std::invalid_argument If data length is -2 or below
1544 @exception std::overflow_error If the amount of data exceeds the amount of
1545 data in the ring buffer
1546 @returns Data from ring buffer
1547 @see append(const std::string, int)
1551 @see remove_to_array
1552 @see remove_to_vector
1553 *///=========================================================================
1554 std::string remove_to_string(
1555 /// Amount of data to remove@n
1558 /// The previously instantiated std::string object to use (the default is to
1559 /// instantiate a new instance of std::string).
1560 std::string data = std::string()) {
1562 // --------------------------------------------------------------------------
1564 // --------------------------------------------------------------------------
1565 if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
1567 // --------------------------------------------------------------------------
1568 // Remove data from ring buffer. If no data is needed, then skip all the
1569 // mutex locking overhead, etc., and return the empty string.
1570 // --------------------------------------------------------------------------
1572 std::lock_guard<std::mutex> guard(__mutex);
1573 if (len == -1) len = __utilized();
1574 if (__utilized() < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds utilized space in ring buffer");
1575 data.resize(len); // Pre-allocate needed memory
1576 for (int i = 0; i < len; i++) {
1577 if (__tail >= __size) __tail = 0; // Wrap-around
1578 data[i] = __buffer[__tail]; // Copy entry
1579 __tail++; // Advance to next entry
1583 // --------------------------------------------------------------------------
1584 // Ring buffer memory allocation maintenance.
1585 // --------------------------------------------------------------------------
1589 } // -x- std::string remove_to_string -x-
1591 /*======================================================================*//**
1593 Remove the specified number of characters from the utilized portion of the
1594 internal ring buffer, and return it as an @c std::string object.
1595 @exception std::invalid_argument If data length is -2 or below
1596 @exception std::overflow_error If the amount of data exceeds the amount of
1597 data in the ring buffer
1598 @returns Data from ring buffer
1599 @see append(const std::vector<char>, int)
1600 @see append(const std::vector<unsigned char>, int)
1604 @see remove_to_array
1605 @see remove_to_string
1606 *///=========================================================================
1607 std::vector<char> remove_to_vector(
1608 /// Amount of data to remove@n
1611 /// The previously instantiated std::string object to use (the default is to
1612 /// instantiate a new instance of std::string).
1613 std::vector<char> data = std::vector<char>()) {
1615 // --------------------------------------------------------------------------
1617 // --------------------------------------------------------------------------
1618 if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
1620 // --------------------------------------------------------------------------
1621 // Remove data from ring buffer. If no data is needed, then skip all the
1622 // mutex locking overhead, etc., and return the empty string.
1623 // --------------------------------------------------------------------------
1625 std::lock_guard<std::mutex> guard(__mutex);
1626 if (len == -1) len = __utilized();
1627 if (__utilized() < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds utilized space in ring buffer");
1628 data.resize(len); // Pre-allocate needed memory
1629 for (int i = 0; i < len; i++) {
1630 if (__tail >= __size) __tail = 0; // Wrap-around
1631 data[i] = __buffer[__tail]; // Copy entry
1632 __tail++; // Advance to next entry
1636 // --------------------------------------------------------------------------
1637 // Ring buffer memory allocation maintenance.
1638 // --------------------------------------------------------------------------
1642 } // -x- std::vector<char> remove_to_vector -x-
1644 /*======================================================================*//**
1646 Flip the order of all entries in the ring buffer.
1647 @returns The same rring object so as to facilitate stacking
1648 *///=========================================================================
1651 { std::lock_guard<std::mutex> guard(__mutex);
1654 for (int i = __utilized() / 2; i > 0; i--) {
1655 if (--h < 0) h = __size - 1; // Wrap-around
1656 if (t >= __size) t = 0; // Wrap-around
1657 carry = __buffer[h];
1658 __buffer[h] = __buffer[t];
1659 __buffer[t] = carry;
1660 t++; // Advance to next entry
1662 } // -x- mutex guard -x-
1664 } // -x- rring* reverse -x-
1666 /*======================================================================*//**
1668 Alter the position of the internal head position, without changing any of the
1669 the pre-existing data in the underlying ring buffer.
1670 @exception std::overflow_error If the new position falls beyond the internal
1671 tail position (or, in other words, it exceeds
1672 the available free space in the ring buffer)
1673 @returns The same rring object so as to facilitate stacking
1680 *///=========================================================================
1682 /// Position adjustment (may also be negative, but only for relative @c type)
1684 /// Type of adjustment@n
1685 /// TRUE = absolute (default)@n
1686 /// FALSE = relative (@c offset may also be negative)
1689 // --------------------------------------------------------------------------
1690 // Absolute position.
1691 // --------------------------------------------------------------------------
1693 if (offset < 0) throw std::overflow_error("Head position adjustment of " + std::to_string(offset) + " cannot be negative");
1694 std::lock_guard<std::mutex> guard(__mutex);
1695 if (offset > __available()) throw std::overflow_error("Head position adjustment of " + std::to_string(offset) + " exceeds available space in ring buffer");
1696 __head = (__head + offset) % __size;
1698 // --------------------------------------------------------------------------
1699 // Relative position.
1700 // --------------------------------------------------------------------------
1702 if (offset > 0) { // Positive adjustment
1703 std::lock_guard<std::mutex> guard(__mutex);
1704 if ( offset > __available()) throw std::overflow_error("Head position adjustment of " + std::to_string(offset) + " exceeds available space in ring buffer");
1705 __head = (__head + offset) % __size;
1706 } else if (offset < 0) { // Negative adjustment
1707 std::lock_guard<std::mutex> guard(__mutex);
1708 if (0 - offset > __utilized() ) throw std::overflow_error("Head position adjustment of " + std::to_string(offset) + " exceeds utilized space in ring buffer");
1710 if (__head >= __size) __head += __size;
1711 } // -x- if offset -x-
1712 } // -x- if type -x-
1715 } // -x- rring* set_head -x-
1717 /*======================================================================*//**
1719 Configure the total size of the ring buffer. The new size cannot be smaller
1720 than the amount of data that's already present in the ring buffer (see the
1721 @ref get_utilized method to find out how much data is present).
1722 @exception std::overflow_error If the new size exceeds the amount of data
1723 that's already present in the ring buffer
1724 @returns The same rring object so as to facilitate stacking
1728 *///=========================================================================
1730 /// New size of ring buffer
1732 { std::lock_guard<std::mutex> guard(__mutex);
1733 if (__utilized() < len) throw std::overflow_error("New size of ring buffer (len=" + std::to_string(len) + ") exceeds utilized amount");
1734 // TODO: Change (increase or decrease) total size of ring buffer, if possible,
1735 // which will entail moving data around if it wraps, and also throwing
1736 // an exception if the reduction is less than the utilized portion.
1738 } // -x- mutex guard -x-
1741 } // -x- rring* set_size -x-
1743 /*======================================================================*//**
1745 Alter the position of the internal tail position, without changing any of the
1746 the pre-existing data in the underlying ring buffer.
1747 @exception std::overflow_error If the new position falls beyond the internal
1748 head position (or, in other words, it exceeds
1749 the available free space in the ring buffer)
1750 @returns The same rring object so as to facilitate stacking
1757 *///=========================================================================
1759 /// Position adjustment (may also be negative, but only for relative @c type)
1761 /// Type of adjustment@n
1762 /// TRUE = absolute (default)@n
1763 /// FALSE = relative (@c offset may also be negative)
1766 // --------------------------------------------------------------------------
1767 // Absolute position.
1768 // --------------------------------------------------------------------------
1770 if (offset < 0) throw std::overflow_error("Tail position adjustment of " + std::to_string(offset) + " cannot be negative");
1771 std::lock_guard<std::mutex> guard(__mutex);
1772 if (offset > __utilized()) throw std::overflow_error("Tail position adjustment of " + std::to_string(offset) + " exceeds utilized space in ring buffer");
1773 __tail = (__tail + offset) % __size;
1775 // --------------------------------------------------------------------------
1776 // Relative position.
1777 // --------------------------------------------------------------------------
1779 if (offset > 0) { // Positive position
1780 std::lock_guard<std::mutex> guard(__mutex);
1781 if ( offset > __utilized() ) throw std::overflow_error("Tail position adjustment of " + std::to_string(offset) + " exceeds utilized space in ring buffer");
1782 __tail = (__tail + offset) % __size;
1783 } else if (offset < 0) { // Negative adjustment
1784 std::lock_guard<std::mutex> guard(__mutex);
1785 if (0 - offset > __available()) throw std::overflow_error("Tail position adjustment of " + std::to_string(offset) + " exceeds available space in ring buffer");
1787 if (__tail >= __size) __tail += __size;
1788 } // -x- if offset -x-
1789 } // -x- if type -x-
1792 } // -x- rring* set_tail -x-
1794 /*======================================================================*//**
1796 Specify whether the internal buffer's memory will be cleared by the
1797 destructor before de-allocating it (default is @c true upon instantiation).
1800 This is an important security feature for when the ring buffer may have been
1801 storing private data because it prevents the leaking of private data to other
1802 process that coincidentally gain access to the same memory area (e.g., by way
1803 of future calls to `malloc()`).
1804 @returns The same rring object so as to facilitate stacking
1805 @see get_wipe_policy
1807 *///=========================================================================
1808 rring* set_wipe_policy(
1809 /// TRUE = clear buffer memory in destructor@n
1810 /// FALSE = don't clear buffer memory in destructor
1811 const bool policy_flag) noexcept {
1812 { std::lock_guard<std::mutex> guard(__mutex);
1813 __wipe_policy = policy_flag;
1814 } // -x- mutex guard -x-
1817 } // -x- rring* set_wipe_policy -x-
1819 /*======================================================================*//**
1821 Configure the automatic expansion policy's incremental block size.
1822 @exception std::overflow_error If the new setting, which reduces the maximum
1823 quantity of blocks, exceeds the available
1824 memory and would otherwise result in losing
1825 some of the utilized entries
1826 @returns The same rring object so as to facilitate stacking
1827 @see get_xp_block_size
1831 *///=========================================================================
1832 rring* set_xp_block_size(
1833 /// Maximum possible number of expansion blocks@n
1834 /// 0 = disable expansion policy
1835 const uint xp_block_size) {
1836 { std::lock_guard<std::mutex> guard(__mutex);
1837 if (xp_block_size == __xp_block_size) return this; // It's pointless to change to the same thing
1839 // --------------------------------------------------------------------------
1840 // Internal variables.
1841 // --------------------------------------------------------------------------
1842 uint old_xp_used_entries = __xp_used * __xp_block_size;
1843 uint new_xp_used_entries = __xp_used * xp_block_size;
1844 size_t base_size = __size - old_xp_used_entries;
1845 size_t utilized = __utilized();
1846//std::cout << "__size=" << __size << " base_size=" << base_size << " old_xp_used_entries=" << old_xp_used_entries << " new_xp_used_entries=" << new_xp_used_entries << " utilized=" << utilized << std::endl;
1848 // --------------------------------------------------------------------------
1849 // Check if size is too small, and throw an exception if it is.
1850 // --------------------------------------------------------------------------
1851 if (utilized > base_size + new_xp_used_entries) throw std::overflow_error("Reducing block size to " + std::to_string(xp_block_size) + " exceeds available memory in ring buffer");
1853 // --------------------------------------------------------------------------
1854 // Update __xp_block_size and then call __adjust_xp to optimize memory block
1856 // --------------------------------------------------------------------------
1857 __xp_block_size = xp_block_size;
1858 __adjust_xp(base_size + new_xp_used_entries);
1860 } // -x- mutex guard -x-
1863 } // -x- rring* set_xp_block_size -x-
1865 /*======================================================================*//**
1867 Configure the automatic expansion policy's maximum number of blocks.
1868 @exception std::overflow_error If the new setting, which reduces the maximum
1869 quantity of blocks, exceeds the available
1870 memory and would otherwise result in losing
1871 some of the utilized entries
1872 @returns The same rring object so as to facilitate stacking
1874 @see set_xp_block_size
1877 *///=========================================================================
1879 /// Maximum possible number of expansion blocks@n
1880 /// 0 = disable expansion policy
1881 const uint xp_max) {
1882 { std::lock_guard<std::mutex> guard(__mutex);
1884 // --------------------------------------------------------------------------
1885 // Increasing the maximum, or keeping the maximum the same, is easy, because
1886 // there's no risk of losing any utilized data.
1887 // --------------------------------------------------------------------------
1888 if (xp_max >= __xp_max) {
1891 // --------------------------------------------------------------------------
1892 // Decreasing the maximum requires making sure we don't lose data.
1893 // --------------------------------------------------------------------------
1895 size_t utilized = __utilized();
1896 uint entries = __size + ((__xp_max - __xp_used) * __xp_block_size) - utilized;// + (utilized % __xp_block_size != 0);
1897std::cout << "__xp_max=" << __xp_max << " xp_max=" << xp_max << "*" << xp_max * __xp_block_size << " entries=" << entries << std::endl;
1898 if (entries > xp_max * __xp_block_size) throw std::overflow_error("Reducing maximum blocks to " + std::to_string(xp_max) + " exceeds available blocks in ring buffer");
1900 } // -x- if xp_max -x-
1901 } // -x- mutex guard -x-
1904 } // -x- rring* set_xp_max -x-
1906 /*======================================================================*//**
1908 Configure the automatic expansion policy's quantity of available blocks that
1909 must be present before attempting a reduction of ring buffer memory.
1910 @returns The same rring object so as to facilitate stacking
1912 @see set_xp_block_size
1915 *///=========================================================================
1917 /// Number of available blocks must be present before attempting reduction
1918 const uint xp_play) noexcept {
1919 { std::lock_guard<std::mutex> guard(__mutex);
1920 __xp_play = xp_play; // Changing this setting doesn't directly impact any data, so just save it
1921 } // -x- mutex guard -x-
1924 } // -x- rring* set_xp_play -x-
1926 /*======================================================================*//**
1928 Convert the entire contents of this ring buffer to an ASCIIZ string in the
1929 form of a @c char* array.
1931 The string returned will need to be `free()`'d after it's no longer needed.
1934 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1935 #include <randolf/rring>
1937 int main(int argc, char *argv[]) {
1938 randolf::rring rb(1024);
1939 rb.append("This is an example.");
1941 std::cout << "\"" << c << "\"" << std::endl;
1943 return EXIT_SUCCESS;
1944 } // -x- int main -x-
1947 @exception std::bad_alloc If @c malloc() fails to allocate memory
1948 @returns Contents of this ring buffer as an ASCII string as a @c char* array.
1950 *///=========================================================================
1951 operator char*() noexcept { return c_str(); } // -x- operator char*() -x-
1953 /*======================================================================*//**
1955 Convert the entire contents of this ring buffer to an @c std::string object.
1958 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1959 #include <string> // std::string
1960 #include <randolf/rring>
1962 int main(int argc, char *argv[]) {
1963 randolf::rring rb(1024);
1964 rb.append("This is an example.");
1966 std::cout << "\"" << s << "\"" << std::endl;
1967 return EXIT_SUCCESS;
1968 } // -x- int main -x-
1971 @returns Contents of this ring buffer as an std::string object
1973 *///=========================================================================
1974 operator std::string() noexcept { return peek_to_string(); } // -x- operator std::string -x-
1976 /*======================================================================*//**
1978 Convert the entire contents of this ring buffer to an @c std::vector<char>
1982 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1983 #include <string> // std::string
1984 #include <vector> // std::vector
1985 #include <randolf/rring>
1987 int main(int argc, char *argv[]) {
1988 randolf::rring rb(1024);
1989 rb.append("This is an example.");
1990 std::vector<char> v = rb;
1991 std::cout << "\"" << std::string(v.begin(), v.end()) << "\"" << std::endl;
1992 return EXIT_SUCCESS;
1993 } // -x- int main -x-
1996 @returns Contents of this ring buffer as an std::vector<char> object
1998 *///=========================================================================
1999 operator std::vector<char>() noexcept { return peek_to_vector(); } // -x- operator std::vector<char> -x-
2001 /*======================================================================*//**
2003 Array-style access to the utilized portion of the ring buffer's contents,
2004 which yields the same result as that of the @ref at() method.
2006 The first element is at index 0.
2007 @throws std::out_of_range if the index is out-of-range
2010 *///=========================================================================
2012 /// Index of character to access (0 = first element; negative index values
2013 /// are calculated in reverse, starting with -1 as the final position)
2015 std::lock_guard<std::mutex> guard(__mutex);
2017 // --------------------------------------------------------------------------
2018 // Internal variables.
2019 // --------------------------------------------------------------------------
2020 int MAX = __utilized();
2022 // --------------------------------------------------------------------------
2024 // --------------------------------------------------------------------------
2025 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");
2027 // --------------------------------------------------------------------------
2028 // Calculate character's position.
2029 // --------------------------------------------------------------------------
2030 index = index >= 0 ? __tail + index : __head + index;
2031 if (index >= __size) index -= __size;
2033 return __buffer[index];
2034 } // -x- char operator[] -x-
2036 /*======================================================================*//**
2038 Support convenient streaming usage with std::cout, std::cerr, and friends.
2041 #include <iostream> // std::cout, std::cerr, std::endl, etc.
2042 #include <string> // std::string
2043 #include <randolf/rring>
2045 int main(int argc, char *argv[]) {
2046 randolf::rring rb(1024);
2047 rb.append("This is an example.");
2048 std::cout << "\"" << rb << "\"" << std::endl;
2049 return EXIT_SUCCESS;
2050 } // -x- int main -x-
2053 @returns Contents of this ring buffer as an std::string object (which will be
2054 de-allocated by the std::string object's destructor upon going out
2057 *///=========================================================================
2058 friend std::ostream& operator<< (
2059 /// Output stream (provided automatically by std::cout and std::cerr)
2061 /// Object class (matched by compiler)
2062 rring& c) noexcept { return o << c.peek_to_string(); } // -x- std::ostream& operator<< -x-
2064 }; // -x- class rring -x-
2066}; // -x- namespace randolf -x-