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 - 2025-Jan-07 v1.00 Added @ref copy_to_rline method
73 - 2025-Jan-09 v1.00 Added optional @c len parameter to various constructor,
74 assign, and append methods for greater flexibility
75 - 2025-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
78 - 2025-Jan-23 v1.00 Performance optimizations
79 - 2025-Feb-03 v1.00 Increased use of references and pointers
80 - 2025-Feb-12 v1.00 Miscellaneous improvements to documentation
81 - 2025-Feb-17 v1.00 Improved optimization of the @ref peek_to_string() and
82 @ref remove_to_string() methods
84 @author Randolf Richardson
85 *///=========================================================================
88 char* __buffer; // Pointer to buffer
89 size_t __size; // Size of buffer // TODO: Add a base_size to ease later calculations with xp
90 size_t __head = 0; // Head (beginning)
91 size_t __tail = 0; // Tail (end)
92 std::mutex __mutex; // Used to add support for multiple threads
94 // --------------------------------------------------------------------------
95 // Dynamic expansion variables.
96 // --------------------------------------------------------------------------
97 uint __xp_block_size; // Expansion increment/decrement block size (default == __size)
98 uint __xp_max = 0; // Maximum possible number of expansion blocks
99 uint __xp_play = 2; // Number of available blocks must be present before attempting reduction
100 uint __xp_used = 0; // Number of expansion increments used
102 // --------------------------------------------------------------------------
104 // --------------------------------------------------------------------------
105 bool __wipe_policy = true; // Destructor memory wipe-out policy
108 /*======================================================================*//**
110 Instantiate an empty ring buffer (without automatic resizing-expansions).
111 @exception std::bad_alloc If @c malloc() fails to allocate memory
112 *///=========================================================================
115 const size_t buffer_size) {
116 __buffer = (char*)::malloc(buffer_size);
117 if (__buffer == nullptr) throw std::bad_alloc(); // Throw exception if __buffer wasn't allocated by malloc
118 __size = buffer_size;
119 __xp_block_size = buffer_size;
120 } // -x- constructor rring -x-
122 /*======================================================================*//**
124 Instantiate an empty ring buffer (with automatic resizing-expansion policies
125 set so the internal buffer will be re-sized to adapt to unexpected allocation
126 needs behind-the-scenes automatically).
127 @exception std::bad_alloc If @c malloc() fails to allocate memory
128 *///=========================================================================
130 /// Initial size of buffer
131 const size_t buffer_size,
132 /// Maximum possible number of expansion blocks@n
133 /// 0 = no expansions / effectively disabling expansion (default)
135 /// Minimum number of available blocks before a reduction can occur
136 const uint xp_play = 2,
137 /// Size of expansion blocks @n
138 /// 0 = use initial @c buffer_size (default) @n
139 const uint xp_block_size = 0) {
140 __buffer = (char*)::malloc(buffer_size);
141 if (__buffer == nullptr) throw std::bad_alloc(); // Throw exception if __buffer wasn't allocated by malloc
142 __size = buffer_size;
144 // --------------------------------------------------------------------------
145 // Dynamic expansion variables.
146 // --------------------------------------------------------------------------
149 __xp_block_size = xp_block_size == 0 ? __size : xp_block_size;
151 } // -x- constructor rring -x-
153 /*======================================================================*//**
155 Free internal memory resources, after clearing buffer's memory (if the policy
156 indicates that it needs to be cleared).
157 *///=========================================================================
159 if (__buffer != nullptr) {
161 // --------------------------------------------------------------------------
162 // Overwrite buffer's memory with NULL if policy indicates it.
163 // --------------------------------------------------------------------------
164 if (__wipe_policy) memset(__buffer, 0, __size);
166 // --------------------------------------------------------------------------
167 // Free buffer's memory resource.
168 // --------------------------------------------------------------------------
171 } // -x- if __buffer -x-
172 } // -x- destructor ~rring -x-
175 // @returns Quantity of entries added (if positive) or removed (if negative)
176 // if ring buffer memory was increased/decreased respectively@n
177 // 0 = there was no change in ring buffer memory
178 /*======================================================================*//**
180 Calculate the number of available entries in the ring buffer, and expand or
181 contract the size of the buffer memory if needed quantity calls for it.
182 @exception std::overflow_error If the quantity of needed entries exceeds the
183 available free space in the ring buffer
184 @returns If `need > 0`, number of entries available, same as __available() @n
185 If `need <= 0`, number of entries removed (0 == none removed)
186 *///=========================================================================
188 /// Number of additional entries to attempt to make available@n
189 /// 0 = attempt to reduce ring buffer memory (with no intention to expand it)
190 const uint need = 0) {
192 // --------------------------------------------------------------------------
193 // Easy checks first.
194 // --------------------------------------------------------------------------
195 if (__xp_max == 0) return 0; // Expandable memory is effectively disabled, so nothing can change
197 // --------------------------------------------------------------------------
198 // Internal variables.
199 // --------------------------------------------------------------------------
200 size_t available = (__head < __tail) ? __tail - __head
201 : __size - (__head - __tail);
203 // --------------------------------------------------------------------------
204 // Reduce memory (as per policies) if there's no need to increase memory.
205 // --------------------------------------------------------------------------
208 // --------------------------------------------------------------------------
209 // Internal variables.
210 // --------------------------------------------------------------------------
211 uint play = __xp_play == 0 ? __xp_block_size : __xp_play * __xp_block_size;
213//std::cout << "play=" << play << " available=" << available << " __xp_used=" << __xp_used << " __xp_play=" << __xp_play << std::endl;
214 // --------------------------------------------------------------------------
215 // If there isn't enough "play," then we don't need to reduce buffer memory.
217 // The reason we don't throw an exception is that reducing ring buffer memory
218 // here is a passive background function, so it's not actually an error when
219 // the conditions for reduction are not satisfied.
220 // --------------------------------------------------------------------------
221 if (available < play /* && __xp_used < __xp_play */) return 0; // No need to reduce buffer memory (because __xp_play is less than what's available)
222 if (available < __xp_used * __xp_block_size) return 0; // No need to reduce buffer memory (because some/all of it is still in use)
224 // --------------------------------------------------------------------------
225 // Allocate less memory and move buffer into it using __defragment (which
226 // takes care of re-allocating the new memory).
228 // Note: Utilized memory is calculated using "__size - available" and then
229 // we add "play" to maintain the minimal amount of available memory
230 // --------------------------------------------------------------------------
231 uint old_size = __size;
232 uint r_blocks = std::min((uint)(available / __xp_block_size), __xp_used);
233 uint new_size = __size - r_blocks * __xp_block_size;
234 __defragment(true, new_size);
235 __xp_used -= r_blocks;
237//std::cout << "old_size=" << old_size << " new_size=" << new_size << std::endl;
238 // --------------------------------------------------------------------------
239 // Return quantity of entries removed since the original need specified 0.
240 // --------------------------------------------------------------------------
241 return old_size - new_size;
245 // --------------------------------------------------------------------------
246 // If there's already enough memory, then simply return the quantity that's
247 // already available.
248 // --------------------------------------------------------------------------
249 if (need <= available) return available; // Return already-calculated quantity of available entries
251 // --------------------------------------------------------------------------
252 // Calculate number of blocks needed, then check that it's possible to
253 // accomodate what's needed or throw an exception if there isn't enough.
254 // --------------------------------------------------------------------------
255 uint xp_blocks_needed = need / __xp_block_size + (need % __xp_block_size != 0); // Total number of blocks needed, mathematically rounded up
256//std::cout << "__size=" << __size << " xp_blocks_needed=" << xp_blocks_needed << " need=" << need << " __xp_max=" << __xp_max << " __xp_used=" << __xp_used << std::endl;
257 if (xp_blocks_needed > __xp_max - __xp_used) throw std::overflow_error("Amount of needed data (quantity=" + std::to_string(need) + ") exceeds expansion limits");
259 // --------------------------------------------------------------------------
260 // Allocate new memory and move buffer into it using __defragment (which
261 // takes care of re-allocating the new memory).
262 // --------------------------------------------------------------------------
263 __defragment(true, __size + __xp_block_size * xp_blocks_needed);
265 // --------------------------------------------------------------------------
266 // If __defragment()'s memory allocation was successful, then it means that
267 // the resizing was successful, and so now it's okay to update any important
269 // --------------------------------------------------------------------------
270 __xp_used = xp_blocks_needed; // Not += (as of 2024-Dec-27)
271//std::cout << "__size=" << __size << " xp_blocks_needed=" << xp_blocks_needed << " need=" << need << " __xp_max=" << __xp_max << " __xp_used=" << __xp_used << std::endl;
273 return __available(); // Return re-calculated quantity of available entries because __defragment probably changed them
275 } // -x- int __adjust_xp -x-
277 /*======================================================================*//**
279 Calculate the number of available entries in the ring buffer.
280 @returns Number of entries available
282 *///=========================================================================
283 size_t __available() noexcept {
284 return (__head < __tail) ? __tail - __head
285 : __size - (__head - __tail);
286 } // -x- size_t __available -x-
288 /*======================================================================*//**
290 Calculate the number of utilized entries in the ring buffer.
291 @returns Number of entries utilized
293 *///=========================================================================
294 size_t __utilized() noexcept {
295 return (__head >= __tail) ? __head - __tail
296 : __size - (__tail - __head);
297 } // -x- size_t __utilized -x-
299 /*======================================================================*//**
301 Copy memory efficiently into the ring buffer with wrap-around (if necessary),
302 and update the internal __head and __tail variables accordingly. (This code
303 is optimized to perform this operation as efficiently as possible because
304 it's used repeatedly, and is essential for better overall performance when
305 used in tight loops that repeatedly append data.)
307 There is no bounds checking, so the length must be correct.
308 *///=========================================================================
310 /// Pointer to data to copy
312 /// Quantity of bytes to copy (cannot be greater than available memory; the
313 /// calling function must have already taken care of this)
315 if (len == 0) return; // Return if there's nothing to do
317 // --------------------------------------------------------------------------
318 // Internal variables.
319 // --------------------------------------------------------------------------
320 size_t smh = __size - __head; // SMH = Size Minus Head
322 // --------------------------------------------------------------------------
323 // Available memory is not wrapped, or length is less than available memory.
324 // --------------------------------------------------------------------------
325 if (__head < __tail || len <= smh) {
326 memcpy(__buffer + __head, data, len);
329 } // -x- if !wrapped -x-
331 // --------------------------------------------------------------------------
332 // Available memory is wrapped, and length is greater than last half of the
334 // --------------------------------------------------------------------------
335 memcpy(__buffer + __head, data, smh); // Copy first portion
336 memcpy(__buffer, data + smh, len - smh); // Copy second portion
337 __head = len - smh; // Adjust head
340 } // -x- void __mem_copy_in -x-
343 /*======================================================================*//**
345 Add one character to the unused portion of the internal ring buffer.
346 @exception std::overflow_error If the amount of data exceeds the available
347 free space in the ring buffer
348 @returns The same rring object so as to facilitate stacking
351 *///=========================================================================
356 // --------------------------------------------------------------------------
357 // Add data to ring buffer. If no data is needed, then skip all the mutex
358 // locking overhead, etc., and return the empty string.
359 // --------------------------------------------------------------------------
360 { std::lock_guard<std::mutex> guard(__mutex);
361 if (__adjust_xp(1) == 0) throw std::overflow_error("Ring buffer is full");
362 if (__head >= __size) __head = 0; // Wrap-around
363 __buffer[__head] = data; // Copy entry
364 __head++; // Advance to next entry
365 } // -x- mutex guard -x-
368 } // -x- rring& append -x-
370 /*======================================================================*//**
372 Add any number of characters that fit within the unused portion of the
373 internal ring buffer.
374 @exception std::invalid_argument If data length is -2 or below
375 @exception std::overflow_error If the amount of data exceeds the available
376 free space in the ring buffer
377 @returns The same rring object so as to facilitate stacking
381 *///=========================================================================
385 /// Length of data (-1 = treat @c data as an ASCIIZ string)
388 // --------------------------------------------------------------------------
390 // --------------------------------------------------------------------------
391 if (len == -1) len = std::strlen(data);
392 else if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
394 // --------------------------------------------------------------------------
395 // Add data to ring buffer. If no data is needed, then skip all the mutex
396 // locking overhead, etc., and return the empty string.
397 // --------------------------------------------------------------------------
399 std::lock_guard<std::mutex> guard(__mutex);
400 if (__adjust_xp(len) < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds available free space in ring buffer");
401 __mem_copy_in(data, len);
405 } // -x- rring& append -x-
407 /*======================================================================*//**
409 Add any number of characters that fit within the unused portion of the
410 internal ring buffer.
411 @exception std::invalid_argument If data length is -2 or below
412 @exception std::length_error If data length is too long
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_string
419 *///=========================================================================
422 const std::string& data,
423 /// Length of data (-1 = rely on data.length())
426 // --------------------------------------------------------------------------
428 // --------------------------------------------------------------------------
429 if (len == -1 ) len = data.length();
430 else if (len < -1 ) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
431 else if (len > data.length()) throw std::length_error("Unrealistic data length of " + std::to_string(len));
433 // --------------------------------------------------------------------------
434 // Add data to ring buffer. If no data is needed, then skip all the mutex
435 // locking overhead, etc., and return the empty string.
436 // --------------------------------------------------------------------------
438 std::lock_guard<std::mutex> guard(__mutex);
439 if (__adjust_xp(len) < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds available free space in ring buffer");
440// for (int i = 0; i < len; i++) {
441// if (__head >= __size) __head = 0; // Wrap-around
442// __buffer[__head] = data[i]; // Copy entry
443// __head++; // Advance to next entry
445 __mem_copy_in(data.data(), len);
449 } // -x- rring& append -x-
451 /*======================================================================*//**
453 Add any number of characters that fit within the unused portion of the
454 internal ring buffer.
455 @exception std::invalid_argument If data length is -2 or below
456 @exception std::overflow_error If the amount of data exceeds the available
457 free space in the ring buffer
458 @returns The same rring object so as to facilitate stacking
461 @see remove_to_vector
462 *///=========================================================================
465 const std::vector<char>& data,
466 /// Length of data (-1 = treat @c data as an ASCIIZ string)
469 // --------------------------------------------------------------------------
471 // --------------------------------------------------------------------------
472 if (len == -1) len = data.size();
473 else if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
475 // --------------------------------------------------------------------------
476 // Add data to ring buffer. If no data is needed, then skip all the mutex
477 // locking overhead, etc., and return the empty string.
478 // --------------------------------------------------------------------------
480 std::lock_guard<std::mutex> guard(__mutex);
481 if (__adjust_xp(len) < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds available free space in ring buffer");
482 __mem_copy_in(data.data(), len);
486 } // -x- rring& append -x-
488 /*======================================================================*//**
490 Add any number of characters that fit within the unused portion of the
491 internal ring buffer.
492 @exception std::invalid_argument If data length is -2 or below
493 @exception std::overflow_error If the amount of data exceeds the available
494 free space in the ring buffer
495 @returns The same rring object so as to facilitate stacking
498 @see remove_to_vector
499 *///=========================================================================
502 const std::vector<unsigned char>& data,
503 /// Length of data (-1 = treat @c data as an ASCIIZ string)
506 // --------------------------------------------------------------------------
508 // --------------------------------------------------------------------------
509 if (len == -1) len = data.size();
510 else if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
512 // --------------------------------------------------------------------------
513 // Add data to ring buffer. If no data is needed, then skip all the mutex
514 // locking overhead, etc., and return the empty string.
515 // --------------------------------------------------------------------------
517 std::lock_guard<std::mutex> guard(__mutex);
518 if (__adjust_xp(len) < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds available free space in ring buffer");
519 __mem_copy_in((const char*)data.data(), len);
523 } // -x- rring& append -x-
525 /*======================================================================*//**
527 Array-style access to the utilized portion of the ring buffer's contents,
528 which yields the same result as that of the @ref at() method.
530 The first element is at index 0.
531 @throws std::out_of_range if the index is out-of-range
534 *///=========================================================================
536 /// Index of character to access (0 = first element; negative index values
537 /// are calculated in reverse, starting with -1 as the final position)
540 { std::lock_guard<std::mutex> guard(__mutex);
541 // --------------------------------------------------------------------------
542 // Internal variables.
543 // --------------------------------------------------------------------------
544 int MAX = __utilized();
546 // --------------------------------------------------------------------------
548 // --------------------------------------------------------------------------
549 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");
551 // --------------------------------------------------------------------------
552 // Calculate character's position.
553 // --------------------------------------------------------------------------
554 index = index >= 0 ? __tail + index : __head + index;
555 if (index >= __size) index -= __size;
556 } // -x- mutex guard -x-
558 return __buffer[index];
561 /*======================================================================*//**
563 Return the contents of the ring buffer as an ASCIIZ string.
565 The string returned will need to be `free()`'d after it's no longer needed.
568 #include <iostream> // std::cout, std::cerr, std::endl, etc.
569 #include <randolf/rring>
571 int main(int argc, char *argv[]) {
572 randolf::rring rb(1024);
573 rb.append("This is an example.");
574 char* c = rb.c_str();
575 std::cout << "\"" << c << "\"" << std::endl;
578 } // -x- int main -x-
582 When using the @ref data parameter in a multi-threaded scenario, the amount
583 of the memory needed should be set to the maximum size of this ring buffer to
584 prevent unintended under-allocation resulting from a simultaneous increase
585 in utilized ring buffer memory (caused by @ref set_size).
586 @exception std::bad_alloc If @c malloc() fails to allocate memory
587 @returns ASCIIZ string
588 @see append(const char*, int)
589 @see append(const std::string, int)
592 *///=========================================================================
594 /// Where to store the ASCIIZ string (including terminating zero, so it is
595 /// imperative that `utilized() + 1` bytes be pre-allocated to avoid writing
596 /// beyond the bounds of the pre-allocated memory)@n
597 /// @c nullptr = allocate the needed memory using @c malloc()
598 char* data = nullptr) noexcept {
600 // --------------------------------------------------------------------------
601 // Internal variables.
602 // --------------------------------------------------------------------------
605 // --------------------------------------------------------------------------
606 // Generate ASCIIZ string.
607 // --------------------------------------------------------------------------
608 { std::lock_guard<std::mutex> guard(__mutex);
610 if (data == nullptr) {
611 data = (char*)::malloc(len + 1);
612 if (data == nullptr) throw std::bad_alloc(); // Throw exception if data wasn't allocated by malloc
613 } // -x- if !data -x-
615 for (int i = 0; i < len; i++) {
616 if (t >= __size) t = 0; // Wrap-around
617 data[i] = __buffer[t]; // Copy entry
618 t++; // Advance to next entry
620 } // -x- mutex guard -x-
621 data[len] = '\0'; // Not "len - 1" because we already allocated "len + 1"
624 } // -x- char* c_str -x-
626 /*======================================================================*//**
628 Copy a line of text into a randolf::rline object. If an EoL sequence is not
629 detected, the data will still fill the randolf::rline object.
631 If providing a pre-existing randolf::rline object, data will be appended to
632 any existing data that's already present.
633 @throws std::invalid_argument If @c len is less than -1
634 @returns Data from ring buffer
635 @see append(const std::string, int)
641 *///=========================================================================
642 randolf::rline* copy_to_rline(
643 /// Maximum quantity of data to duplicate@n
646 /// Pointer to an already-instantiated @ref randolf::rline object to use@n
647 /// nullptr = instantiate a new @ref randolf::rline object (this will need to
648 /// be `delete`'d when it's no longer needed)
649 randolf::rline* data = nullptr) {
651 // --------------------------------------------------------------------------
653 // --------------------------------------------------------------------------
654 if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
655 if (data == nullptr) data = new randolf::rline();
657 // --------------------------------------------------------------------------
658 // Duplicate data from ring buffer, quickly (in one or two moves).
659 // --------------------------------------------------------------------------
660 { std::lock_guard<std::mutex> guard(__mutex);
662 case -1: // Copy everything
663 if (__head >= __tail) { // Not wrapped
664 data->append(__buffer + __tail, __utilized());
666 int n = __size - __tail;
667 data->append(__buffer + __tail, std::min(len, n));
668 if (n < len) data->append(__buffer, len - n);
669 } // -x- if not_wrapped -x-
670 case 0: // Copy nothing at all
672 default: // Copy specified quantity
673 int utilized = __utilized();
674 if (utilized < len) len = utilized; // throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds utilized space in ring buffer");
675 if (__head >= __tail) { // Not wrapped
676 data->append(__buffer + __tail, std::min(len, utilized));
678 int n = __size - __tail;
679 data->append(__buffer + __tail, std::min(len, n));
680 if (n < len) data->append(__buffer, len - n);
681 } // -x- if not_wrapped -x-
682 } // -x- switch len -x-
683 } // -x- mutex guard -x-
686 } // -x- randolf::rline* copy_to_rline -x-
688 /*======================================================================*//**
690 Returns a pointer to the underlying ring buffer's array. If the "defragment"
691 flag is cleared then the beginning of the array may not be the same as where
692 the data actually begins (and ends) within the buffer (see the @c warning
696 This is the live data, which should not be accessed or modified directly; use
697 the @ref data_bam() and @ref data_bam_adjust() methods instead when there is
698 a need to access or modify the backing array directly.
699 @exception std::bad_alloc If @c malloc() fails to allocate memory (this can
700 only occur when the "defragment" flag is set)
701 @returns Pointer to buffer (not null terminated)
709 *///=========================================================================
711 /// Whether to defragment the data first@n
712 /// @c TRUE = fuly defragment so that data begins at position 0 (default) @n
713 /// @c FALSE = don't defragment the data (this is an advanced feature that
714 /// will require knowing how to use the @ref data_bam method, and
715 /// also the @ref data_bam method if adding or removing data)
716 bool defragment_flag = true) noexcept {
717 std::lock_guard<std::mutex> guard(__mutex);
718 if (defragment_flag) defragment(true);
720 } // -x- char* data -x-
722 /*======================================================================*//**
724 Returns the lengths and addresses of available memory. There may be only one
725 block of memory, or two, depending on whether the available memory straddles
726 the boundary of the ring buffer.
728 This is the nearest equivalent of the @ref data() methods provided in classes
729 such as @c std::string and @c std::vector, but with the nuance of supporting
730 the underpinnings of this ring buffer wherein the utilized portions may be
731 stored at both the end and beginning of the buffer memory.
733 The intention of this method is to provide an opportunity to directly update
734 the contents of the ring buffer's data section in an efficient manner (e.g.,
735 such as by reading file or socket data directly into memory without having to
736 perform separate data copying operations, etc.).
738 Upon updating a portion of the available-data block, the @c head will also
739 need to be updated accordingly, for which the @ref data_bam_adjust method is
741 @exception std::bad_alloc If @c malloc() fails to allocate memory
742 @returns Pointer to @ref rring_bam structure
752 *///=========================================================================
754 /// Pointer to @ref rring_bam structure (that's already been allocated)@n
755 /// @c nullptr (default) = allocate memory for this structure with @c malloc
756 /// (which will need to be `free`'d when it's no longer needed)
757 rring_bam* bam = nullptr) noexcept {
759 // --------------------------------------------------------------------------
760 // Internal variables.
761 // --------------------------------------------------------------------------
762 if (bam == nullptr) {
763 bam = (rring_bam*)::malloc(sizeof(rring_bam));
764 if (bam == nullptr) throw std::bad_alloc(); // Throw exception if bam wasn't allocated by malloc
767 { std::lock_guard<std::mutex> guard(__mutex);
769 // --------------------------------------------------------------------------
770 // Configure utilized data block pointer(s) and length(s).
771 // --------------------------------------------------------------------------
772 bam->block1 = __buffer + __tail;
773 if (__head >= __tail) { // Not wrapped
774 bam->block1_size = __head - __tail;
775 bam->block2 = nullptr;
776 bam->block2_size = 0;
778 bam->block1_size = __size - __tail;
779 bam->block2 = __size - __tail == 0 ? nullptr : __buffer;
780 bam->block2_size = __head;
781 } // -x- if __head -x-
783 // --------------------------------------------------------------------------
784 // Configure available data block pointer(s) and length(s).
785 // --------------------------------------------------------------------------
786 bam->avail1 = __buffer + __head;
787 if (__head < __tail) { // Not wrapped
788 bam->avail1_size = __tail - __head;
789 bam->avail2 = nullptr;
790 bam->avail2_size = 0;
792 bam->avail1_size = __size - __head;
793 bam->avail2 = __tail == 0 ? nullptr : __buffer;
794 bam->avail2_size = __tail;
795 } // -x- if __head -x-
797 } // -x- mutex guard -x-
800 } // -x- rring_bam* data_bam -x-
802 /*======================================================================*//**
804 Arbitrarily change the head and tail positions in the ring buffer. This is
805 intended for use in conjunction with appending data directly to the ring
806 buffer after the @ref data_bam method.
807 @exception std::overflow_error If either adjustment would result in causing
809 @returns The same rring object so as to facilitate stacking
815 *///=========================================================================
816 rring& data_bam_adjust(
817 /// New internal head position
819 /// New internal tail position
820 const size_t tail = 0) {
822 // --------------------------------------------------------------------------
823 // Set new head and tail positions.
824 // --------------------------------------------------------------------------
825 { std::lock_guard<std::mutex> guard(__mutex);
828 } // -x- mutex guard -x-
831 } // -x- rring& data_bam_adjust -x-
834 /*======================================================================*//**
836 Internal method used primarily by the defragment() and resize() methods.
838 A full defragmentation always results in the tail changing to position 0.
840 TO DO: Return to code in _archive/2024-Dec-22/ which is nearly finished, and
841 attempt to get the algorithm working for the last few bytes so that
842 the memory allocation approach we use now won't be needed (and also
843 therefore eliminates the wipe_policy implementation step).
844 @exception std::length_error If the new @c buffer_size is less than the total
845 quantity of utilized entries (which would result
846 in the truncation of data)
847 @exception std::bad_alloc If @c malloc() fails to allocate memory
850 *///=========================================================================
852 /// Whether to defrag when the utilized block isn't straddling the internal
853 /// memory buffer boundary@n
854 /// TRUE = always fully defragment (default) @n
855 /// FALSE = only defragment if utilized block straddles memory boundary
857 /// Total size of new memory buffer to be allocated (used in resizing so that
858 /// we only allocate the new buffer once {here} rather than twice {here, and
859 /// in any of the methods that @ref resize the internal buffer})
860 const size_t buffer_size) {
862 // --------------------------------------------------------------------------
863 // Internal variables.
864 // --------------------------------------------------------------------------
865 const size_t utilized = __utilized();
867 // --------------------------------------------------------------------------
868 // Perform quick checks if size isn't increasing.
869 // --------------------------------------------------------------------------
870 if (buffer_size == __size) {
872 // --------------------------------------------------------------------------
873 // Already fully optimized, so don't even start.
874 // --------------------------------------------------------------------------
875 if (__tail == 0) return;
877 // --------------------------------------------------------------------------
878 // If not wrapped around ring buffer (and not resizing), optimization is a
879 // fast-and-easy matter of shifting all the elements without re-allocating
880 // the ring buffer memory.
881 // --------------------------------------------------------------------------
882 if (__tail <= __head) {
883 if (!full) return; // No work to be done
884 for (size_t i = 0; i < utilized; i++) // Move data
885 __buffer[i] = __buffer[__tail++];
889 } // -x- if __tail -x-
891 // --------------------------------------------------------------------------
892 // Size reduction sanity check.
893 // --------------------------------------------------------------------------
894 } else if (buffer_size < utilized) {
896 // --------------------------------------------------------------------------
897 // Prevent truncation by throwing a length_error exception.
898 // --------------------------------------------------------------------------
899 throw std::length_error("Unrealistic buffer size reduction to " + std::to_string(buffer_size));
901 } // -x- if buffer_size -x-
903 // --------------------------------------------------------------------------
904 // Allocate new internal buffer.
905 // --------------------------------------------------------------------------
906 void* data = (char*)::malloc(buffer_size); // Allocate new buffer to the same as as the current one
907 if (data == nullptr) throw std::bad_alloc(); // Throw exception if data wasn't allocated by malloc
909 // --------------------------------------------------------------------------
910 // Copy data to new buffer that is the same size as the current one.
911 // --------------------------------------------------------------------------
912 for (int i = 0; i < utilized; i++) {
913 if (__tail >= __size) __tail = 0; // Wrap-around
914 ((char*)data)[i] = __buffer[__tail]; // Copy entry
915 __tail++; // Advance to next entry
918 // --------------------------------------------------------------------------
919 // De-allocate old internal buffer, and wipe old data before de-allocation if
920 // the policy warrants it.
921 // --------------------------------------------------------------------------
922 if (__wipe_policy) memset(__buffer, 0, __size);//for (int i = 0; i < __size; i++) __buffer[i] = 0;
925 // --------------------------------------------------------------------------
926 // Set new buffer pointer and internal variables.
927 // --------------------------------------------------------------------------
928 __buffer = (char*)data; // Update pointer to internal buffer
931 __size = buffer_size;
934 } // -x- void __defragment -x-
937 /*======================================================================*//**
939 Reorganizes the utilized memory in the ring buffer memory by moving the tail
940 to the beginning. Defragmentation is completed in the most efficient manner,
941 depending on what needs to be done. If data wraps around the buffer memory,
942 then new memory will be allocated and data will be moved to it (old memory
943 will also be cleared if the wipe policy is in effect).
945 A full defragmentation always results in the tail changing to position 0.
946 @exception std::length_error If the new @c buffer_size is less than the total
947 quantity of utilized entries (which would result
948 in the truncation of data)
949 @exception std::bad_alloc If @c malloc() fails to allocate memory
950 @returns The same rring object so as to facilitate stacking
956 *///=========================================================================
958 /// Whether to defrag when the utilized block isn't straddling the internal
959 /// memory buffer boundary@n
960 /// TRUE = always fully defragment (default) @n
961 /// FALSE = only defragment if utilized block straddles memory boundary (this
962 /// favours reducing processor overhead if all that's wanted is to
963 /// have one block of memory to contend with in the BAM, hence it's
964 /// also what the @ref set_size() method uses for better performance
965 /// overall because resizing most likely occurs when the need for CPU
966 /// cycles are more critical, so we're doing our part by reducing our
968 const bool full = true) {
969 { std::lock_guard<std::mutex> guard(__mutex);
970 __defragment(full, __size);
971 } // -x- mutex guard -x-
973 } // -x- rring& defragment -x-
975 /*======================================================================*//**
977 Discard data from the ring buffer.
978 @throws std::invalid_argument If @c len is less than -1
979 @returns Number of entries that were discarded
981 *///=========================================================================
983 /// Number of bytes to discard@n
984 /// -1 = all utilized data that remains will be discarded (default)
985 int len = -1) noexcept {
986 if (len == 0) return 0; // Do nothing
988 { std::lock_guard<std::mutex> guard(__mutex);
989 if (len == -1) len = __size;
990 else if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
991 discarded = __utilized();
993 // --------------------------------------------------------------------------
994 // Discard everything because len is greater than or equal to the amount of
995 // data in the ring buffer.
996 // --------------------------------------------------------------------------
997 if (len >= discarded) { // Discard everything if len is too large
1000 __adjust_xp(); // Ring buffer memory allocation maintenance
1001 return len; // Return quantity that was actually discarded
1004 // --------------------------------------------------------------------------
1005 // Adjust __tail according to amount of data to discard. Calculating this is
1006 // enormously faster than looping through the buffer.
1007 // --------------------------------------------------------------------------
1008 if (__head >= __tail || __size - __tail < len) { // Not wrapping, so adjustment is easy
1010 } else { // Wrapping, so change tail to discard size minus what's discarded from the end
1011 __tail = len - (__size - __tail);
1012 } // -x- if __head -x-
1014 // --------------------------------------------------------------------------
1015 // Ring buffer memory allocation maintenance.
1016 // --------------------------------------------------------------------------
1019 } // -x- mutex guard -x-
1021 return discarded - len; // Return quantity that was actually discarded
1022 } // -x- int discard -x-
1024 /*======================================================================*//**
1026 Indicates whether the ring buffer is empty.
1027 @returns TRUE = empty@n
1033 *///=========================================================================
1034 bool empty() noexcept {
1035 std::lock_guard<std::mutex> guard(__mutex);
1036 return __head == __tail;
1037 } // -x- bool empty -x-
1039 /*======================================================================*//**
1041 Obtains a one-line ASCII art depiction of a map of the ring buffer's memory
1042 usage, wherein the utilized and available portions are represented by the `|`
1043 (pipe) and `.` (period) characters, respectively. These characters are
1044 configurable and may each contain more than one character, which is
1045 particularly useful for text-mode console output in a typical Linux shell in
1046 which @c ANSI or @c AVATAR codes may be used to present these characters or
1047 sequences in different colours, although there may be other uses for this
1049 @exception std::invalid_argument If length is -2 or below
1050 @returns One-line ASCII depiction of ring buffer's memory map
1055 *///=========================================================================
1056 std::string get_ascii_map(
1057 /// Desired size of ASCII map@n
1058 /// -1 = Use size of entire ring buffer
1060 /// Character(s) representing utilized entries
1061 const std::string u = "|",
1062 /// Character(s) representing available entries
1063 const std::string a = ".") noexcept {
1065 // --------------------------------------------------------------------------
1066 // Internal variables, which will be copied .
1067 // --------------------------------------------------------------------------
1071 { std::lock_guard<std::mutex> guard(__mutex);
1075 } // -x- mutex guard -x-
1078 // --------------------------------------------------------------------------
1080 // --------------------------------------------------------------------------
1081 if (len == -1) len = size;
1082 if (len == 0) return map;
1083 if (len <= -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
1085 // --------------------------------------------------------------------------
1086 // Calculate output.
1087 // --------------------------------------------------------------------------
1089 head = head * (len / size);
1090 tail = tail * (len / size);
1092 head = head / (size / len);
1093 tail = tail / (size / len);
1095 map.reserve(len); // Pre-allocate the exact amount of underlying memory needed
1097 // --------------------------------------------------------------------------
1099 // --------------------------------------------------------------------------
1100 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
1101 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
1104 } // -x- std::string get_ascii_map -x-
1106 /*======================================================================*//**
1108 Calculate the number of available entries in the ring buffer.
1109 @returns Number of entries available
1112 @see get_xp_blocks_available
1113 *///=========================================================================
1114 size_t get_available() noexcept {
1115 std::lock_guard<std::mutex> guard(__mutex);
1116 return __available();
1117 } // -x- size_t get_available -x-
1119 /*======================================================================*//**
1121 Obtains the base size of the ring buffer, regardless of how much is utilized
1122 or available, and without any of the expansion policy limits.
1123 @returns Maximum potential size of ring buffer
1131 *///=========================================================================
1132 size_t get_base_size() noexcept {
1133 std::lock_guard<std::mutex> guard(__mutex);
1134 return __size - (__xp_used * __xp_block_size);
1135 } // -x- size_t get_base_size -x-
1137 /*======================================================================*//**
1139 Obtains the position of the head in the ring buffer.
1140 @returns Position of head in ring buffer
1148 *///=========================================================================
1149 size_t get_head() noexcept {
1150 std::lock_guard<std::mutex> guard(__mutex);
1152 } // -x- size_t get_head -x-
1154 /*======================================================================*//**
1156 Obtains the potential total size of the ring buffer, including both its
1157 utilized and available portions, including the maximum limit of what the
1158 expansion policies can extend the internal ring buffer's size to.
1159 @returns Maximum potential size of ring buffer
1168 *///=========================================================================
1169 size_t get_max_size() noexcept {
1170 std::lock_guard<std::mutex> guard(__mutex);
1171 return __size + ((__xp_max - __xp_used) * __xp_block_size);
1172 } // -x- size_t get_max_size -x-
1174 /*======================================================================*//**
1176 Obtains the total size of the current ring buffer, including both its
1177 utilized and available portions.
1178 @returns Total current size of ring buffer
1186 *///=========================================================================
1187 size_t get_size() noexcept {
1188 std::lock_guard<std::mutex> guard(__mutex);
1190 } // -x- size_t get_size -x-
1192 /*======================================================================*//**
1194 Obtains the position of the tail in the ring buffer.
1195 @returns Position of tail in ring buffer
1203 *///=========================================================================
1204 size_t get_tail() noexcept {
1205 std::lock_guard<std::mutex> guard(__mutex);
1207 } // -x- size_t get_tail -x-
1209 /*======================================================================*//**
1211 Calculate the number of utilized entries in the ring buffer.
1212 @returns Number of entries utilized
1215 @see get_xp_blocks_utilized
1216 *///=========================================================================
1217 size_t get_utilized() noexcept {
1218 std::lock_guard<std::mutex> guard(__mutex);
1219 return __utilized();
1220 } // -x- size_t get_utilized -x-
1222 /*======================================================================*//**
1224 Indicate whether internal buffer's memory will be cleared by the destructor
1225 before de-allocating it (default is @c true upon instantiation).
1228 This is an important security feature for when the ring buffer may have been
1229 storing private data because it prevents the leaking of private data to other
1230 process that coincidentally gain access to the same memory area (e.g., by way
1231 of future calls to `malloc()`).
1232 @returns Post-wipe policy (TRUE = clear / FALSE = don't clear)
1233 @see set_wipe_policy
1235 *///=========================================================================
1236 bool get_wipe_policy() noexcept {
1237 std::lock_guard<std::mutex> guard(__mutex);
1238 return __wipe_policy;
1239 } // -x- bool get_wipe_policy -x-
1241 /*======================================================================*//**
1243 Obtain block size in expansion policy.
1244 @returns Expansion policy's block size
1247 @see set_xp_block_size
1249 *///=========================================================================
1250 size_t get_xp_block_size() noexcept {
1251 std::lock_guard<std::mutex> guard(__mutex);
1252 return __xp_block_size;
1253 } // -x- size_t get_xp_block_size -x-
1255 /*======================================================================*//**
1257 Obtain quantity of blocks available for use in buffer ring memory expansions.
1258 @returns Quantity of expansion blocks available
1260 @see get_xp_block_size
1261 @see get_xp_blocks_utilized
1265 *///=========================================================================
1266 size_t get_xp_blocks_available() noexcept {
1267 std::lock_guard<std::mutex> guard(__mutex);
1268 return __xp_max - __xp_used;
1269 } // -x- size_t get_xp_blocks_available -x-
1271 /*======================================================================*//**
1273 Obtain quantity of blocks utilized utilized for buffer ring memory expansion.
1274 @returns Quantity of expansion blocks utilized
1276 @see get_xp_block_size
1277 @see get_xp_blocks_available
1281 *///=========================================================================
1282 size_t get_xp_blocks_utilized() noexcept {
1283 std::lock_guard<std::mutex> guard(__mutex);
1285 } // -x- size_t get_xp_blocks_utilized -x-
1287 /*======================================================================*//**
1289 Obtain maximum block quantity in expansion policy.
1290 @returns Expansion policy's maximum block quantity
1291 @see get_xp_block_size
1292 @see get_xp_blocks_available
1293 @see get_xp_blocks_utilized
1297 *///=========================================================================
1298 size_t get_xp_max() noexcept {
1299 std::lock_guard<std::mutex> guard(__mutex);
1301 } // -x- size_t get_xp_max -x-
1303 /*======================================================================*//**
1305 Obtain minimum quantity of blocks available before automatic expansion block
1307 @returns Expansion policy's play setting
1308 @see get_xp_block_size
1309 @see get_xp_blocks_available
1310 @see get_xp_blocks_utilized
1314 *///=========================================================================
1315 size_t get_xp_play() noexcept {
1316 std::lock_guard<std::mutex> guard(__mutex);
1318 } // -x- size_t get_xp_play -x-
1320 /*======================================================================*//**
1322 Duplicate a single character from the utilized portion of the internal ring
1323 buffer, and return it as a @c char primitive.
1324 @exception std::overflow_error If the amount of data exceeds the amount of
1325 data in the ring buffer
1326 @returns Data from ring buffer
1327 @see append(const char)
1333 *///=========================================================================
1335 std::lock_guard<std::mutex> guard(__mutex);
1337 // --------------------------------------------------------------------------
1339 // --------------------------------------------------------------------------
1340 if (__utilized() == 0) throw std::overflow_error("Ring buffer is empty");
1342 // --------------------------------------------------------------------------
1343 // Duplicate the last byte of data from the ring buffer.
1344 // --------------------------------------------------------------------------
1345 return __buffer[__tail]; // Copy entry
1347 } // -x- char peek -x-
1349 /*======================================================================*//**
1351 Duplicate the specified number of characters from the utilized portion of the
1352 internal ring buffer, and return them as a @c char* array.
1354 The string returned will need to be `free()`'d after it's no longer needed.
1355 @exception std::bad_alloc If @c malloc() fails to allocate memory
1356 @exception std::overflow_error If the amount of data exceeds the amount of
1357 data in the ring buffer
1358 @returns Data from ring buffer
1359 @see append(const char*, int)
1364 @see remove_to_array
1365 *///=========================================================================
1366 char* peek_to_array(
1367 /// Amount of data to duplicate
1369 /// Where to store the data@n
1370 /// @c nullptr = allocate the needed memory using @c malloc()
1371 char* data = nullptr) {
1373 // --------------------------------------------------------------------------
1374 // Internal variables.
1375 // --------------------------------------------------------------------------
1376 if (data == nullptr) {
1377 data = (char*)::malloc(len);
1378 if (data == nullptr) throw std::bad_alloc(); // Throw exception if data wasn't allocated by malloc
1379 } // -x- if !data -x-
1381 // --------------------------------------------------------------------------
1382 // Remove data from ring buffer.
1383 // --------------------------------------------------------------------------
1384 { std::lock_guard<std::mutex> guard(__mutex);
1386 for (int i = 0; i < len; i++) {
1387 if (t >= __size) t = 0; // Wrap-around
1388 data[i] = __buffer[t]; // Copy entry
1389 t++; // Advance to next entry
1391 } // -x- mutex guard -x-
1394 } // -x- char* peek_to_array -x-
1396 /*======================================================================*//**
1398 Duplicate the specified number of characters from the utilized portion of the
1399 internal ring buffer, and return it as an @c std::string object.
1400 @exception std::invalid_argument If data length is -2 or below
1401 @exception std::overflow_error If the amount of data exceeds the amount of
1402 data in the ring buffer
1403 @returns Data from ring buffer
1404 @see append(const std::string, int)
1409 @see remove_to_string
1410 *///=========================================================================
1411 std::string peek_to_string(
1412 /// Amount of data to duplicate@n
1415 /// Pointer to the previously instantiated @c std::string object to use@n
1416 /// @c nullptr = instantiate a new instance of @c std::string (default)
1417 const std::string* data = nullptr) {
1419 // --------------------------------------------------------------------------
1421 // --------------------------------------------------------------------------
1422 if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
1424 // --------------------------------------------------------------------------
1425 // Internal variables.
1426 // --------------------------------------------------------------------------
1427 std::string new_data;
1429 // --------------------------------------------------------------------------
1430 // Duplicate data from ring buffer. If no data is needed, then skip all the
1431 // mutex locking overhead, etc., and return the empty string.
1432 // --------------------------------------------------------------------------
1434 if (data != nullptr) new_data.assign(*data);
1435 std::lock_guard<std::mutex> guard(__mutex);
1436 if (len == -1) len = __utilized();
1437 if (__utilized() < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds utilized space in ring buffer");
1438 new_data.resize(len); // Pre-allocate needed memory so that we can write to it directly
1440 for (int i = 0; i < len; i++) {
1441 if (t >= __size) t = 0; // Wrap-around
1442 new_data[i] = __buffer[t]; // Copy entry
1443 t++; // Advance to next entry
1448 } // -x- std::string peek_to_string -x-
1450 /*======================================================================*//**
1452 Duplicate the specified number of characters from the utilized portion of the
1453 internal ring buffer, and return it as an @c std::string object.
1454 @exception std::invalid_argument If data length is -2 or below
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 std::vector<char>, int)
1459 @see append(const std::vector<unsigned char>, int)
1464 @see remove_to_vector
1465 *///=========================================================================
1466 std::vector<char> peek_to_vector(
1467 /// Amount of data to duplicate@n
1470 /// The previously instantiated std::string object to use (the default is to
1471 /// instantiate a new instance of std::string).
1472 std::vector<char> data = std::vector<char>()) {
1474 // --------------------------------------------------------------------------
1476 // --------------------------------------------------------------------------
1477 if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
1479 // --------------------------------------------------------------------------
1480 // Duplicate data from ring buffer. If no data is needed, then skip all the
1481 // mutex locking overhead, etc., and return the empty string.
1482 // --------------------------------------------------------------------------
1484 std::lock_guard<std::mutex> guard(__mutex);
1485 if (len == -1) len = __utilized();
1486 if (__utilized() < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds utilized space in ring buffer");
1487 data.resize(len); // Pre-allocate needed memory
1489 for (int i = 0; i < len; i++) {
1490 if (t >= __size) t = 0; // Wrap-around
1491 data[i] = __buffer[t]; // Copy entry
1492 t++; // Advance to next entry
1497 } // -x- std::vector<char> peek_to_vector -x-
1499 /*======================================================================*//**
1501 Remove one character from the utilized portion of the internal ring buffer,
1503 @exception std::overflow_error If the amount of data exceeds the amount of
1504 data in the ring buffer
1505 @returns Data from ring buffer
1506 @see append(const char)
1509 @see remove_to_array
1510 @see remove_to_string
1511 @see remove_to_vector
1512 *///=========================================================================
1515 // --------------------------------------------------------------------------
1516 // Remove data from ring buffer. If no data is needed, then skip all the
1517 // mutex locking overhead, etc., and return the empty string.
1518 // --------------------------------------------------------------------------
1520 { std::lock_guard<std::mutex> guard(__mutex);
1521 if (__utilized() == 0) throw std::overflow_error("Ring buffer is empty");
1522 if (__tail >= __size) __tail = 0; // Wrap-around
1523 ch = __buffer[__tail]; // Copy entry
1524 __tail++; // Advance to next entry
1525 } // -x- mutex guard -x-
1527 // --------------------------------------------------------------------------
1528 // Ring buffer memory allocation maintenance.
1529 // --------------------------------------------------------------------------
1533 } // -x- char remove -x-
1535 /*======================================================================*//**
1537 Remove the specified number of characters from the utilized portion of the
1538 internal ring buffer, and return them as a @c char* array.
1540 The string returned will need to be `free()`'d after it's no longer needed.
1541 @exception std::bad_alloc If @c malloc() fails to allocate memory
1542 @exception std::overflow_error If the amount of data exceeds the amount of
1543 data in the ring buffer
1544 @returns Data from ring buffer
1545 @see append(const char*, int)
1549 @see remove_to_string
1550 @see remove_to_vector
1551 *///=========================================================================
1552 char* remove_to_array(
1553 /// Maximum quantity of data to move@n
1555 /// Where to store the data@n
1556 /// @c nullptr = allocate the needed memory using @c malloc()
1557 char* data = nullptr) {
1559 // --------------------------------------------------------------------------
1560 // Internal variables.
1561 // --------------------------------------------------------------------------
1562 if (data == nullptr) {
1563 data = (char*)::malloc(len);
1564 if (data == nullptr) throw std::bad_alloc(); // Throw exception if data wasn't allocated by malloc
1565 } // -x- if !data -x-
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 // --------------------------------------------------------------------------
1571 { std::lock_guard<std::mutex> guard(__mutex);
1572 for (int i = 0; i < len; i++) {
1573 if (__tail >= __size) __tail = 0; // Wrap-around
1574 data[i] = __buffer[__tail]; // Copy entry
1575 __tail++; // Advance to next entry
1577 } // -x- mutex guard -x-
1579 // --------------------------------------------------------------------------
1580 // Ring buffer memory allocation maintenance.
1581 // --------------------------------------------------------------------------
1585 } // -x- char* remove_to_array -x-
1587 /*======================================================================*//**
1589 Remove the specified number of characters from the utilized portion of the
1590 internal ring buffer, and return it as an @c std::string object.
1591 @exception std::invalid_argument If data length is -2 or below
1592 @exception std::overflow_error If the amount of data exceeds the amount of
1593 data in the ring buffer
1594 @returns Data from ring buffer
1595 @see append(const std::string, int)
1599 @see remove_to_array
1600 @see remove_to_vector
1601 *///=========================================================================
1602 std::string remove_to_string(
1603 /// Amount of data to remove@n
1606 /// Pointer to the previously instantiated @c std::string object to use@n
1607 /// @c nullptr = instantiate a new instance of @c std::string (default)
1608 const std::string* data = nullptr) {
1610 // --------------------------------------------------------------------------
1612 // --------------------------------------------------------------------------
1613 if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
1615 // --------------------------------------------------------------------------
1616 // Internal variables.
1617 // --------------------------------------------------------------------------
1618 std::string new_data;
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 if (data != nullptr) new_data.assign(*data);
1626 std::lock_guard<std::mutex> guard(__mutex);
1627 if (len == -1) len = __utilized();
1628 if (__utilized() < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds utilized space in ring buffer");
1629 new_data.resize(len); // Pre-allocate needed memory
1630 for (int i = 0; i < len; i++) {
1631 if (__tail >= __size) __tail = 0; // Wrap-around
1632 new_data[i] = __buffer[__tail]; // Copy entry
1633 __tail++; // Advance to next entry
1637 // --------------------------------------------------------------------------
1638 // Ring buffer memory allocation maintenance.
1639 // --------------------------------------------------------------------------
1643 } // -x- std::string remove_to_string -x-
1645 /*======================================================================*//**
1647 Remove the specified number of characters from the utilized portion of the
1648 internal ring buffer, and return it as an @c std::string object.
1649 @exception std::invalid_argument If data length is -2 or below
1650 @exception std::overflow_error If the amount of data exceeds the amount of
1651 data in the ring buffer
1652 @returns Data from ring buffer
1653 @see append(const std::vector<char>, int)
1654 @see append(const std::vector<unsigned char>, int)
1658 @see remove_to_array
1659 @see remove_to_string
1660 *///=========================================================================
1661 std::vector<char> remove_to_vector(
1662 /// Amount of data to remove@n
1665 /// The previously instantiated std::string object to use (the default is to
1666 /// instantiate a new instance of std::string).
1667 std::vector<char> data = std::vector<char>()) {
1669 // --------------------------------------------------------------------------
1671 // --------------------------------------------------------------------------
1672 if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
1674 // --------------------------------------------------------------------------
1675 // Remove data from ring buffer. If no data is needed, then skip all the
1676 // mutex locking overhead, etc., and return the empty string.
1677 // --------------------------------------------------------------------------
1679 std::lock_guard<std::mutex> guard(__mutex);
1680 if (len == -1) len = __utilized();
1681 if (__utilized() < len) throw std::overflow_error("Amount of data (len=" + std::to_string(len) + ") exceeds utilized space in ring buffer");
1682 data.resize(len); // Pre-allocate needed memory
1683 for (int i = 0; i < len; i++) {
1684 if (__tail >= __size) __tail = 0; // Wrap-around
1685 data[i] = __buffer[__tail]; // Copy entry
1686 __tail++; // Advance to next entry
1690 // --------------------------------------------------------------------------
1691 // Ring buffer memory allocation maintenance.
1692 // --------------------------------------------------------------------------
1696 } // -x- std::vector<char> remove_to_vector -x-
1698 /*======================================================================*//**
1700 Flip the order of all entries in the ring buffer.
1701 @returns The same rring object so as to facilitate stacking
1702 *///=========================================================================
1705 { std::lock_guard<std::mutex> guard(__mutex);
1708 for (int i = __utilized() / 2; i > 0; i--) {
1709 if (--h < 0) h = __size - 1; // Wrap-around
1710 if (t >= __size) t = 0; // Wrap-around
1711 carry = __buffer[h];
1712 __buffer[h] = __buffer[t];
1713 __buffer[t] = carry;
1714 t++; // Advance to next entry
1716 } // -x- mutex guard -x-
1718 } // -x- rring& reverse -x-
1720 /*======================================================================*//**
1722 Alter the position of the internal head position, without changing any of the
1723 the pre-existing data in the underlying ring buffer.
1724 @exception std::overflow_error If the new position falls beyond the internal
1725 tail position (or, in other words, it exceeds
1726 the available free space in the ring buffer)
1727 @returns The same rring object so as to facilitate stacking
1734 *///=========================================================================
1736 /// Position adjustment (may also be negative, but only for relative @c type)
1738 /// Type of adjustment@n
1739 /// TRUE = absolute (default)@n
1740 /// FALSE = relative (@c offset may also be negative)
1741 const bool type = true) {
1743 // --------------------------------------------------------------------------
1744 // Absolute position.
1745 // --------------------------------------------------------------------------
1747 if (offset < 0) throw std::overflow_error("Head position adjustment of " + std::to_string(offset) + " cannot be negative");
1748 std::lock_guard<std::mutex> guard(__mutex);
1749 if (offset > __available()) throw std::overflow_error("Head position adjustment of " + std::to_string(offset) + " exceeds available space in ring buffer");
1750 __head = (__head + offset) % __size;
1752 // --------------------------------------------------------------------------
1753 // Relative position.
1754 // --------------------------------------------------------------------------
1756 if (offset > 0) { // Positive adjustment
1757 std::lock_guard<std::mutex> guard(__mutex);
1758 if ( offset > __available()) throw std::overflow_error("Head position adjustment of " + std::to_string(offset) + " exceeds available space in ring buffer");
1759 __head = (__head + offset) % __size;
1760 } else if (offset < 0) { // Negative adjustment
1761 std::lock_guard<std::mutex> guard(__mutex);
1762 if (0 - offset > __utilized() ) throw std::overflow_error("Head position adjustment of " + std::to_string(offset) + " exceeds utilized space in ring buffer");
1764 if (__head >= __size) __head += __size;
1765 } // -x- if offset -x-
1766 } // -x- if type -x-
1769 } // -x- rring& set_head -x-
1771 /*======================================================================*//**
1773 Configure the total size of the ring buffer. The new size cannot be smaller
1774 than the amount of data that's already present in the ring buffer (see the
1775 @ref get_utilized method to find out how much data is present).
1776 @exception std::overflow_error If the new size exceeds the amount of data
1777 that's already present in the ring buffer
1778 @returns The same rring object so as to facilitate stacking
1782 *///=========================================================================
1784 /// New size of ring buffer
1786 { std::lock_guard<std::mutex> guard(__mutex);
1787 if (__utilized() < len) throw std::overflow_error("New size of ring buffer (len=" + std::to_string(len) + ") exceeds utilized amount");
1788 // TODO: Change (increase or decrease) total size of ring buffer, if possible,
1789 // which will entail moving data around if it wraps, and also throwing
1790 // an exception if the reduction is less than the utilized portion.
1792 } // -x- mutex guard -x-
1795 } // -x- rring& set_size -x-
1797 /*======================================================================*//**
1799 Alter the position of the internal tail position, without changing any of the
1800 the pre-existing data in the underlying ring buffer.
1801 @exception std::overflow_error If the new position falls beyond the internal
1802 head position (or, in other words, it exceeds
1803 the available free space in the ring buffer)
1804 @returns The same rring object so as to facilitate stacking
1811 *///=========================================================================
1813 /// Position adjustment (may also be negative, but only for relative @c type)
1815 /// Type of adjustment@n
1816 /// TRUE = absolute (default)@n
1817 /// FALSE = relative (@c offset may also be negative)
1818 const bool type = true) {
1820 // --------------------------------------------------------------------------
1821 // Absolute position.
1822 // --------------------------------------------------------------------------
1824 if (offset < 0) throw std::overflow_error("Tail position adjustment of " + std::to_string(offset) + " cannot be negative");
1825 std::lock_guard<std::mutex> guard(__mutex);
1826 if (offset > __utilized()) throw std::overflow_error("Tail position adjustment of " + std::to_string(offset) + " exceeds utilized space in ring buffer");
1827 __tail = (__tail + offset) % __size;
1829 // --------------------------------------------------------------------------
1830 // Relative position.
1831 // --------------------------------------------------------------------------
1833 if (offset > 0) { // Positive position
1834 std::lock_guard<std::mutex> guard(__mutex);
1835 if ( offset > __utilized() ) throw std::overflow_error("Tail position adjustment of " + std::to_string(offset) + " exceeds utilized space in ring buffer");
1836 __tail = (__tail + offset) % __size;
1837 } else if (offset < 0) { // Negative adjustment
1838 std::lock_guard<std::mutex> guard(__mutex);
1839 if (0 - offset > __available()) throw std::overflow_error("Tail position adjustment of " + std::to_string(offset) + " exceeds available space in ring buffer");
1841 if (__tail >= __size) __tail += __size;
1842 } // -x- if offset -x-
1843 } // -x- if type -x-
1846 } // -x- rring& set_tail -x-
1848 /*======================================================================*//**
1850 Specify whether the internal buffer's memory will be cleared by the
1851 destructor before de-allocating it (default is @c true upon instantiation).
1854 This is an important security feature for when the ring buffer may have been
1855 storing private data because it prevents the leaking of private data to other
1856 process that coincidentally gain access to the same memory area (e.g., by way
1857 of future calls to `malloc()`).
1858 @returns The same rring object so as to facilitate stacking
1859 @see get_wipe_policy
1861 *///=========================================================================
1862 rring& set_wipe_policy(
1863 /// TRUE = clear buffer memory in destructor@n
1864 /// FALSE = don't clear buffer memory in destructor
1865 const bool policy_flag) noexcept {
1866 { std::lock_guard<std::mutex> guard(__mutex);
1867 __wipe_policy = policy_flag;
1868 } // -x- mutex guard -x-
1871 } // -x- rring& set_wipe_policy -x-
1873 /*======================================================================*//**
1875 Configure the automatic expansion policy's incremental block size.
1876 @exception std::overflow_error If the new setting, which reduces the maximum
1877 quantity of blocks, exceeds the available
1878 memory and would otherwise result in losing
1879 some of the utilized entries
1880 @returns The same rring object so as to facilitate stacking
1881 @see get_xp_block_size
1885 *///=========================================================================
1886 rring& set_xp_block_size(
1887 /// Maximum possible number of expansion blocks@n
1888 /// 0 = disable expansion policy
1889 const uint xp_block_size) {
1890 { std::lock_guard<std::mutex> guard(__mutex);
1891 if (xp_block_size == __xp_block_size) return *this; // It's pointless to change to the same thing
1893 // --------------------------------------------------------------------------
1894 // Internal variables.
1895 // --------------------------------------------------------------------------
1896 uint old_xp_used_entries = __xp_used * __xp_block_size;
1897 uint new_xp_used_entries = __xp_used * xp_block_size;
1898 size_t base_size = __size - old_xp_used_entries;
1899 size_t utilized = __utilized();
1900//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;
1902 // --------------------------------------------------------------------------
1903 // Check if size is too small, and throw an exception if it is.
1904 // --------------------------------------------------------------------------
1905 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");
1907 // --------------------------------------------------------------------------
1908 // Update __xp_block_size and then call __adjust_xp to optimize memory block
1910 // --------------------------------------------------------------------------
1911 __xp_block_size = xp_block_size;
1912 __adjust_xp(base_size + new_xp_used_entries);
1914 } // -x- mutex guard -x-
1917 } // -x- rring& set_xp_block_size -x-
1919 /*======================================================================*//**
1921 Configure the automatic expansion policy's maximum number of blocks.
1922 @exception std::overflow_error If the new setting, which reduces the maximum
1923 quantity of blocks, exceeds the available
1924 memory and would otherwise result in losing
1925 some of the utilized entries
1926 @returns The same rring object so as to facilitate stacking
1928 @see set_xp_block_size
1931 *///=========================================================================
1933 /// Maximum possible number of expansion blocks@n
1934 /// 0 = disable expansion policy
1935 const uint xp_max) {
1936 { std::lock_guard<std::mutex> guard(__mutex);
1938 // --------------------------------------------------------------------------
1939 // Increasing the maximum, or keeping the maximum the same, is easy, because
1940 // there's no risk of losing any utilized data.
1941 // --------------------------------------------------------------------------
1942 if (xp_max >= __xp_max) {
1945 // --------------------------------------------------------------------------
1946 // Decreasing the maximum requires making sure we don't lose data.
1947 // --------------------------------------------------------------------------
1949 size_t utilized = __utilized();
1950 uint entries = __size + ((__xp_max - __xp_used) * __xp_block_size) - utilized;// + (utilized % __xp_block_size != 0);
1951std::cout << "__xp_max=" << __xp_max << " xp_max=" << xp_max << "*" << xp_max * __xp_block_size << " entries=" << entries << std::endl;
1952 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");
1954 } // -x- if xp_max -x-
1955 } // -x- mutex guard -x-
1958 } // -x- rring& set_xp_max -x-
1960 /*======================================================================*//**
1962 Configure the automatic expansion policy's quantity of available blocks that
1963 must be present before attempting a reduction of ring buffer memory.
1964 @returns The same rring object so as to facilitate stacking
1966 @see set_xp_block_size
1969 *///=========================================================================
1971 /// Number of available blocks must be present before attempting reduction
1972 const uint xp_play) noexcept {
1973 { std::lock_guard<std::mutex> guard(__mutex);
1974 __xp_play = xp_play; // Changing this setting doesn't directly impact any data, so just save it
1975 } // -x- mutex guard -x-
1978 } // -x- rring& set_xp_play -x-
1980 /*======================================================================*//**
1982 Convert the entire contents of this ring buffer to an ASCIIZ string in the
1983 form of a @c char* array.
1985 The string returned will need to be `free()`'d after it's no longer needed.
1988 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1989 #include <randolf/rring>
1991 int main(int argc, char *argv[]) {
1992 randolf::rring rb(1024);
1993 rb.append("This is an example.");
1995 std::cout << "\"" << c << "\"" << std::endl;
1997 return EXIT_SUCCESS;
1998 } // -x- int main -x-
2001 @exception std::bad_alloc If @c malloc() fails to allocate memory
2002 @returns Contents of this ring buffer as an ASCII string as a @c char* array.
2004 *///=========================================================================
2005 operator char*() noexcept {
2007 } // -x- operator char*() -x-
2009 /*======================================================================*//**
2011 Convert the entire contents of this ring buffer to an @c std::string object.
2014 #include <iostream> // std::cout, std::cerr, std::endl, etc.
2015 #include <string> // std::string
2016 #include <randolf/rring>
2018 int main(int argc, char *argv[]) {
2019 randolf::rring rb(1024);
2020 rb.append("This is an example.");
2022 std::cout << "\"" << s << "\"" << std::endl;
2023 return EXIT_SUCCESS;
2024 } // -x- int main -x-
2027 @returns Contents of this ring buffer as an std::string object
2029 *///=========================================================================
2030 operator std::string() noexcept {
2031 return peek_to_string();
2032 } // -x- operator std::string -x-
2034 /*======================================================================*//**
2036 Convert the entire contents of this ring buffer to an @c std::vector<char>
2040 #include <iostream> // std::cout, std::cerr, std::endl, etc.
2041 #include <string> // std::string
2042 #include <vector> // std::vector
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::vector<char> v = rb;
2049 std::cout << "\"" << std::string(v.begin(), v.end()) << "\"" << std::endl;
2050 return EXIT_SUCCESS;
2051 } // -x- int main -x-
2054 @returns Contents of this ring buffer as an std::vector<char> object
2056 *///=========================================================================
2057 operator std::vector<char>() noexcept {
2058 return peek_to_vector();
2059 } // -x- operator std::vector<char> -x-
2061 /*======================================================================*//**
2063 Array-style access to the utilized portion of the ring buffer's contents,
2064 which yields the same result as that of the @ref at() method.
2066 The first element is at index 0.
2067 @throws std::out_of_range if the index is out-of-range
2070 *///=========================================================================
2072 /// Index of character to access (0 = first element; negative index values
2073 /// are calculated in reverse, starting with -1 as the final position)
2075 std::lock_guard<std::mutex> guard(__mutex);
2077 // --------------------------------------------------------------------------
2078 // Internal variables.
2079 // --------------------------------------------------------------------------
2080 int MAX = __utilized();
2082 // --------------------------------------------------------------------------
2084 // --------------------------------------------------------------------------
2085 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");
2087 // --------------------------------------------------------------------------
2088 // Calculate character's position.
2089 // --------------------------------------------------------------------------
2090 index = index >= 0 ? __tail + index : __head + index;
2091 if (index >= __size) index -= __size;
2093 return __buffer[index];
2094 } // -x- char operator[] -x-
2096 /*======================================================================*//**
2098 Support convenient streaming usage with std::cout, std::cerr, and friends.
2101 #include <iostream> // std::cout, std::cerr, std::endl, etc.
2102 #include <string> // std::string
2103 #include <randolf/rring>
2105 int main(int argc, char *argv[]) {
2106 randolf::rring rb(1024);
2107 rb.append("This is an example.");
2108 std::cout << "\"" << rb << "\"" << std::endl;
2109 return EXIT_SUCCESS;
2110 } // -x- int main -x-
2113 @returns Contents of this ring buffer as an std::string object (which will be
2114 de-allocated by the std::string object's destructor upon going out
2117 *///=========================================================================
2118 friend std::ostream& operator<< (
2119 /// Output stream (provided automatically by std::cout and std::cerr)
2121 /// Object class (matched by compiler)
2122 rring& c) noexcept {
2123 return o << c.peek_to_string();
2124 } // -x- std::ostream& operator<< -x-
2126 }; // -x- class rring -x-
2128}; // -x- namespace randolf -x-