randolf.ca  1.00
Randolf Richardson's C++ classes
Loading...
Searching...
No Matches
rring
1#pragma once
2
3#include <algorithm> // std::min and std::max
4#include <cstring>
5#include <mutex>
6#include <new>
7#include <tuple>
8#include <vector>
9
10#include <randolf/rline>
11#include <randolf/rring_bam>
12
13namespace randolf {
14
15 /*======================================================================*//**
16 @brief
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.
20
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).
27
28 @par Use case
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.
36
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.).
50
51 @par Conventions
52 Lower-case letters "rb" are regularly used in partial example code to
53 represent an instantiated rring object.
54
55 An ASCIIZ string is a C-string (char* array) that includes a terminating null
56 (0) character at the end.
57
58 @par History
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
68 policy parameters
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
83 @version 1.00
84 @author Randolf Richardson
85 *///=========================================================================
86 class rring {
87 private:
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
93
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
101
102 // --------------------------------------------------------------------------
103 // Policy flags.
104 // --------------------------------------------------------------------------
105 bool __wipe_policy = true; // Destructor memory wipe-out policy
106
107 public:
108 /*======================================================================*//**
109 @brief
110 Instantiate an empty ring buffer (without automatic resizing-expansions).
111 @exception std::bad_alloc If @c malloc() fails to allocate memory
112 *///=========================================================================
113 rring(
114 /// Size of buffer
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-
121
122 /*======================================================================*//**
123 @brief
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 *///=========================================================================
129 rring(
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)
134 const uint xp_max,
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;
143
144 // --------------------------------------------------------------------------
145 // Dynamic expansion variables.
146 // --------------------------------------------------------------------------
147 __xp_max = xp_max;
148 __xp_play = xp_play;
149 __xp_block_size = xp_block_size == 0 ? __size : xp_block_size;
150
151 } // -x- constructor rring -x-
152
153 /*======================================================================*//**
154 @brief
155 Free internal memory resources, after clearing buffer's memory (if the policy
156 indicates that it needs to be cleared).
157 *///=========================================================================
158 ~rring() noexcept {
159 if (__buffer != nullptr) {
160
161 // --------------------------------------------------------------------------
162 // Overwrite buffer's memory with NULL if policy indicates it.
163 // --------------------------------------------------------------------------
164 if (__wipe_policy) memset(__buffer, 0, __size);
165
166 // --------------------------------------------------------------------------
167 // Free buffer's memory resource.
168 // --------------------------------------------------------------------------
169 ::free(__buffer);
170
171 } // -x- if __buffer -x-
172 } // -x- destructor ~rring -x-
173
174 private:
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 /*======================================================================*//**
179 @brief
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 *///=========================================================================
187 uint __adjust_xp(
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) {
191
192 // --------------------------------------------------------------------------
193 // Easy checks first.
194 // --------------------------------------------------------------------------
195 if (__xp_max == 0) return 0; // Expandable memory is effectively disabled, so nothing can change
196
197 // --------------------------------------------------------------------------
198 // Internal variables.
199 // --------------------------------------------------------------------------
200 size_t available = (__head < __tail) ? __tail - __head
201 : __size - (__head - __tail);
202
203 // --------------------------------------------------------------------------
204 // Reduce memory (as per policies) if there's no need to increase memory.
205 // --------------------------------------------------------------------------
206 if (need == 0) {
207
208 // --------------------------------------------------------------------------
209 // Internal variables.
210 // --------------------------------------------------------------------------
211 uint play = __xp_play == 0 ? __xp_block_size : __xp_play * __xp_block_size;
212
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.
216 //
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)
223
224 // --------------------------------------------------------------------------
225 // Allocate less memory and move buffer into it using __defragment (which
226 // takes care of re-allocating the new memory).
227 //
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;
236
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;
242
243 } // -x- if need -x-
244
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
250
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");
258
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);
264
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
268 // variables.
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;
272
273 return __available(); // Return re-calculated quantity of available entries because __defragment probably changed them
274
275 } // -x- int __adjust_xp -x-
276
277 /*======================================================================*//**
278 @brief
279 Calculate the number of available entries in the ring buffer.
280 @returns Number of entries available
281 @see get_available
282 *///=========================================================================
283 size_t __available() noexcept {
284 return (__head < __tail) ? __tail - __head
285 : __size - (__head - __tail);
286 } // -x- size_t __available -x-
287
288 /*======================================================================*//**
289 @brief
290 Calculate the number of utilized entries in the ring buffer.
291 @returns Number of entries utilized
292 @see get_utilized
293 *///=========================================================================
294 size_t __utilized() noexcept {
295 return (__head >= __tail) ? __head - __tail
296 : __size - (__tail - __head);
297 } // -x- size_t __utilized -x-
298
299 /*======================================================================*//**
300 @brief
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.)
306 @warning
307 There is no bounds checking, so the length must be correct.
308 *///=========================================================================
309 void __mem_copy_in(
310 /// Pointer to data to copy
311 const char* data,
312 /// Quantity of bytes to copy (cannot be greater than available memory; the
313 /// calling function must have already taken care of this)
314 const size_t len) {
315 if (len == 0) return; // Return if there's nothing to do
316
317 // --------------------------------------------------------------------------
318 // Internal variables.
319 // --------------------------------------------------------------------------
320 size_t smh = __size - __head; // SMH = Size Minus Head
321
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);
327 __head += len;
328 return;
329 } // -x- if !wrapped -x-
330
331 // --------------------------------------------------------------------------
332 // Available memory is wrapped, and length is greater than last half of the
333 // available memory.
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
338
339 return;
340 } // -x- void __mem_copy_in -x-
341
342 public:
343 /*======================================================================*//**
344 @brief
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
349 @see peek
350 @see remove
351 *///=========================================================================
352 rring& append(
353 /// Data to add
354 const char data) {
355
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-
366
367 return *this;
368 } // -x- rring& append -x-
369
370 /*======================================================================*//**
371 @brief
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
378 @see c_str
379 @see peek_to_array
380 @see remove_to_array
381 *///=========================================================================
382 rring& append(
383 /// Data to add
384 const char* data,
385 /// Length of data (-1 = treat @c data as an ASCIIZ string)
386 int len = -1) {
387
388 // --------------------------------------------------------------------------
389 // Syntax checks.
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));
393
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 // --------------------------------------------------------------------------
398 if (len > 0) {
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);
402 } // -x- if len -x-
403
404 return *this;
405 } // -x- rring& append -x-
406
407 /*======================================================================*//**
408 @brief
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
416 @see c_str
417 @see peek_to_string
418 @see remove_to_string
419 *///=========================================================================
420 rring& append(
421 /// Data to add
422 const std::string& data,
423 /// Length of data (-1 = rely on data.length())
424 int len = -1) {
425
426 // --------------------------------------------------------------------------
427 // Syntax checks.
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));
432
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 // --------------------------------------------------------------------------
437 if (len > 0) {
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
444// } // -x- for i -x-
445 __mem_copy_in(data.data(), len);
446 } // -x- if len -x-
447
448 return *this;
449 } // -x- rring& append -x-
450
451 /*======================================================================*//**
452 @brief
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
459 @see c_str
460 @see peek_to_vector
461 @see remove_to_vector
462 *///=========================================================================
463 rring& append(
464 /// Data to add
465 const std::vector<char>& data,
466 /// Length of data (-1 = treat @c data as an ASCIIZ string)
467 int len = -1) {
468
469 // --------------------------------------------------------------------------
470 // Syntax checks.
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));
474
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 // --------------------------------------------------------------------------
479 if (len > 0) {
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);
483 } // -x- if len -x-
484
485 return *this;
486 } // -x- rring& append -x-
487
488 /*======================================================================*//**
489 @brief
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
496 @see c_str
497 @see peek_to_vector
498 @see remove_to_vector
499 *///=========================================================================
500 rring& append(
501 /// Data to add
502 const std::vector<unsigned char>& data,
503 /// Length of data (-1 = treat @c data as an ASCIIZ string)
504 int len = -1) {
505
506 // --------------------------------------------------------------------------
507 // Syntax checks.
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));
511
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 // --------------------------------------------------------------------------
516 if (len > 0) {
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);
520 } // -x- if len -x-
521
522 return *this;
523 } // -x- rring& append -x-
524
525 /*======================================================================*//**
526 @brief
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.
529
530 The first element is at index 0.
531 @throws std::out_of_range if the index is out-of-range
532 @returns char
533 @see operator[]
534 *///=========================================================================
535 char at(
536 /// Index of character to access (0 = first element; negative index values
537 /// are calculated in reverse, starting with -1 as the final position)
538 int index) {
539
540 { std::lock_guard<std::mutex> guard(__mutex);
541 // --------------------------------------------------------------------------
542 // Internal variables.
543 // --------------------------------------------------------------------------
544 int MAX = __utilized();
545
546 // --------------------------------------------------------------------------
547 // Syntax checks.
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");
550
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-
557
558 return __buffer[index];
559 } // -x- char at -x-
560
561 /*======================================================================*//**
562 @brief
563 Return the contents of the ring buffer as an ASCIIZ string.
564 @post
565 The string returned will need to be `free()`'d after it's no longer needed.
566
567 @code{.cpp}
568 #include <iostream> // std::cout, std::cerr, std::endl, etc.
569 #include <randolf/rring>
570
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;
576 ::free(c);
577 return EXIT_SUCCESS;
578 } // -x- int main -x-
579 @endcode
580
581 @warning
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)
590 @see peek_to_array
591 @see peek_to_string
592 *///=========================================================================
593 char* c_str(
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 {
599
600 // --------------------------------------------------------------------------
601 // Internal variables.
602 // --------------------------------------------------------------------------
603 size_t len;
604
605 // --------------------------------------------------------------------------
606 // Generate ASCIIZ string.
607 // --------------------------------------------------------------------------
608 { std::lock_guard<std::mutex> guard(__mutex);
609 len = __utilized();
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-
614 size_t t = __tail;
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
619 } // -x- for i -x-
620 } // -x- mutex guard -x-
621 data[len] = '\0'; // Not "len - 1" because we already allocated "len + 1"
622
623 return data;
624 } // -x- char* c_str -x-
625
626 /*======================================================================*//**
627 @brief
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.
630 @note
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)
636 @see c_str
637 @see peek
638 @see peek_to_array
639 @see peek_to_string
640 @see peek_to_vector
641 *///=========================================================================
642 randolf::rline* copy_to_rline(
643 /// Maximum quantity of data to duplicate@n
644 /// -1 = all data
645 int len = -1,
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) {
650
651 // --------------------------------------------------------------------------
652 // Syntax checks.
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();
656
657 // --------------------------------------------------------------------------
658 // Duplicate data from ring buffer, quickly (in one or two moves).
659 // --------------------------------------------------------------------------
660 { std::lock_guard<std::mutex> guard(__mutex);
661 switch (len) {
662 case -1: // Copy everything
663 if (__head >= __tail) { // Not wrapped
664 data->append(__buffer + __tail, __utilized());
665 } else {
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
671 break;
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));
677 } else {
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-
684
685 return data;
686 } // -x- randolf::rline* copy_to_rline -x-
687
688 /*======================================================================*//**
689 @brief
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
693 hereunder).
694
695 @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)
702 @see data_bam
703 @see data_bam_adjust
704 @see defragment
705 @see get_available
706 @see get_utilized
707 @see set_size
708 @qualifier ADVANCED
709 *///=========================================================================
710 char* data(
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);
719 return __buffer;
720 } // -x- char* data -x-
721
722 /*======================================================================*//**
723 @brief
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.
727
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.
732 @note
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.).
737 @warning
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
740 provided.
741 @exception std::bad_alloc If @c malloc() fails to allocate memory
742 @returns Pointer to @ref rring_bam structure
743 @see data
744 @see data_bam_adjust
745 @see empty
746 @see get_available
747 @see get_head
748 @see get_tail
749 @see get_utilized
750 @see rring_bam
751 @qualifier ADVANCED
752 *///=========================================================================
753 rring_bam* data_bam(
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 {
758
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
765 } // -x- if !bam -x-
766
767 { std::lock_guard<std::mutex> guard(__mutex);
768
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;
777 } else { // Wrapped
778 bam->block1_size = __size - __tail;
779 bam->block2 = __size - __tail == 0 ? nullptr : __buffer;
780 bam->block2_size = __head;
781 } // -x- if __head -x-
782
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;
791 } else { // Wrapped
792 bam->avail1_size = __size - __head;
793 bam->avail2 = __tail == 0 ? nullptr : __buffer;
794 bam->avail2_size = __tail;
795 } // -x- if __head -x-
796
797 } // -x- mutex guard -x-
798
799 return bam;
800 } // -x- rring_bam* data_bam -x-
801
802 /*======================================================================*//**
803 @brief
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
808 an overflow
809 @returns The same rring object so as to facilitate stacking
810 @see data
811 @see data_bam
812 @see set_head
813 @see set_tail
814 @qualifier ADVANCED
815 *///=========================================================================
816 rring& data_bam_adjust(
817 /// New internal head position
818 const size_t head,
819 /// New internal tail position
820 const size_t tail = 0) {
821
822 // --------------------------------------------------------------------------
823 // Set new head and tail positions.
824 // --------------------------------------------------------------------------
825 { std::lock_guard<std::mutex> guard(__mutex);
826 __head = head;
827 __tail = tail;
828 } // -x- mutex guard -x-
829
830 return *this;
831 } // -x- rring& data_bam_adjust -x-
832
833 private:
834 /*======================================================================*//**
835 @brief
836 Internal method used primarily by the defragment() and resize() methods.
837
838 A full defragmentation always results in the tail changing to position 0.
839
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
848 @see defragment
849 @see set_size
850 *///=========================================================================
851 void __defragment(
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
856 const bool full,
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) {
861
862 // --------------------------------------------------------------------------
863 // Internal variables.
864 // --------------------------------------------------------------------------
865 const size_t utilized = __utilized();
866
867 // --------------------------------------------------------------------------
868 // Perform quick checks if size isn't increasing.
869 // --------------------------------------------------------------------------
870 if (buffer_size == __size) {
871
872 // --------------------------------------------------------------------------
873 // Already fully optimized, so don't even start.
874 // --------------------------------------------------------------------------
875 if (__tail == 0) return;
876
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++];
886 __head = utilized;
887 __tail = 0;
888 return;
889 } // -x- if __tail -x-
890
891 // --------------------------------------------------------------------------
892 // Size reduction sanity check.
893 // --------------------------------------------------------------------------
894 } else if (buffer_size < utilized) {
895
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));
900
901 } // -x- if buffer_size -x-
902
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
908
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
916 } // -x- for i -x-
917
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;
923 ::free(__buffer);
924
925 // --------------------------------------------------------------------------
926 // Set new buffer pointer and internal variables.
927 // --------------------------------------------------------------------------
928 __buffer = (char*)data; // Update pointer to internal buffer
929 __head = utilized;
930 __tail = 0;
931 __size = buffer_size;
932
933 return;
934 } // -x- void __defragment -x-
935
936 public:
937 /*======================================================================*//**
938 @brief
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).
944
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
951 @see data
952 @see data_bam
953 @see get_ascii_map
954 @see get_wipe_policy
955 @see set_size
956 *///=========================================================================
957 rring& defragment(
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
967 /// CPU load)
968 const bool full = true) {
969 { std::lock_guard<std::mutex> guard(__mutex);
970 __defragment(full, __size);
971 } // -x- mutex guard -x-
972 return *this;
973 } // -x- rring& defragment -x-
974
975 /*======================================================================*//**
976 @brief
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
980 @see remove
981 *///=========================================================================
982 size_t discard(
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
987 size_t discarded;
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();
992
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
998 __head = 0;
999 __tail = 0;
1000 __adjust_xp(); // Ring buffer memory allocation maintenance
1001 return len; // Return quantity that was actually discarded
1002 } // -x- if len -x-
1003
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
1009 __tail += len;
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-
1013
1014 // --------------------------------------------------------------------------
1015 // Ring buffer memory allocation maintenance.
1016 // --------------------------------------------------------------------------
1017 __adjust_xp();
1018
1019 } // -x- mutex guard -x-
1020
1021 return discarded - len; // Return quantity that was actually discarded
1022 } // -x- int discard -x-
1023
1024 /*======================================================================*//**
1025 @brief
1026 Indicates whether the ring buffer is empty.
1027 @returns TRUE = empty@n
1028 FALSE = not empty
1029 @see data_bam
1030 @see get_available
1031 @see get_utilized
1032 @see get_size
1033 *///=========================================================================
1034 bool empty() noexcept {
1035 std::lock_guard<std::mutex> guard(__mutex);
1036 return __head == __tail;
1037 } // -x- bool empty -x-
1038
1039 /*======================================================================*//**
1040 @brief
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
1048 too.
1049 @exception std::invalid_argument If length is -2 or below
1050 @returns One-line ASCII depiction of ring buffer's memory map
1051 @see get_available
1052 @see get_head
1053 @see get_tail
1054 @see get_utilized
1055 *///=========================================================================
1056 std::string get_ascii_map(
1057 /// Desired size of ASCII map@n
1058 /// -1 = Use size of entire ring buffer
1059 int len = 65,
1060 /// Character(s) representing utilized entries
1061 const std::string u = "|",
1062 /// Character(s) representing available entries
1063 const std::string a = ".") noexcept {
1064
1065 // --------------------------------------------------------------------------
1066 // Internal variables, which will be copied .
1067 // --------------------------------------------------------------------------
1068 double head;
1069 double tail;
1070 double size;
1071 { std::lock_guard<std::mutex> guard(__mutex);
1072 head = __head;
1073 tail = __tail;
1074 size = __size;
1075 } // -x- mutex guard -x-
1076 std::string map;
1077
1078 // --------------------------------------------------------------------------
1079 // Syntax checks.
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));
1084
1085 // --------------------------------------------------------------------------
1086 // Calculate output.
1087 // --------------------------------------------------------------------------
1088 if (len >= size) {
1089 head = head * (len / size);
1090 tail = tail * (len / size);
1091 } else {
1092 head = head / (size / len);
1093 tail = tail / (size / len);
1094 } // -x- if len -x-
1095 map.reserve(len); // Pre-allocate the exact amount of underlying memory needed
1096
1097 // --------------------------------------------------------------------------
1098 // Generate output.
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
1102
1103 return map;
1104 } // -x- std::string get_ascii_map -x-
1105
1106 /*======================================================================*//**
1107 @brief
1108 Calculate the number of available entries in the ring buffer.
1109 @returns Number of entries available
1110 @see data_bam
1111 @see get_utilized
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-
1118
1119 /*======================================================================*//**
1120 @brief
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
1124 @see empty
1125 @see get_available
1126 @see get_max_size
1127 @see get_size
1128 @see get_utilized
1129 @see set_size
1130 @qualifier ADVANCED
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-
1136
1137 /*======================================================================*//**
1138 @brief
1139 Obtains the position of the head in the ring buffer.
1140 @returns Position of head in ring buffer
1141 @see data_bam
1142 @see get_available
1143 @see get_tail
1144 @see get_utilized
1145 @see set_head
1146 @see set_tail
1147 @qualifier ADVANCED
1148 *///=========================================================================
1149 size_t get_head() noexcept {
1150 std::lock_guard<std::mutex> guard(__mutex);
1151 return __head;
1152 } // -x- size_t get_head -x-
1153
1154 /*======================================================================*//**
1155 @brief
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
1160 @see empty
1161 @see get_available
1162 @see get_base_size
1163 @see get_max_size
1164 @see get_size
1165 @see get_utilized
1166 @see set_size
1167 @qualifier ADVANCED
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-
1173
1174 /*======================================================================*//**
1175 @brief
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
1179 @see empty
1180 @see get_available
1181 @see get_base_size
1182 @see get_max_size
1183 @see get_utilized
1184 @see set_size
1185 @qualifier ADVANCED
1186 *///=========================================================================
1187 size_t get_size() noexcept {
1188 std::lock_guard<std::mutex> guard(__mutex);
1189 return __size;
1190 } // -x- size_t get_size -x-
1191
1192 /*======================================================================*//**
1193 @brief
1194 Obtains the position of the tail in the ring buffer.
1195 @returns Position of tail in ring buffer
1196 @see data_bam
1197 @see get_available
1198 @see get_head
1199 @see get_utilized
1200 @see set_head
1201 @see set_tail
1202 @qualifier ADVANCED
1203 *///=========================================================================
1204 size_t get_tail() noexcept {
1205 std::lock_guard<std::mutex> guard(__mutex);
1206 return __tail;
1207 } // -x- size_t get_tail -x-
1208
1209 /*======================================================================*//**
1210 @brief
1211 Calculate the number of utilized entries in the ring buffer.
1212 @returns Number of entries utilized
1213 @see data_bam
1214 @see get_available
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-
1221
1222 /*======================================================================*//**
1223 @brief
1224 Indicate whether internal buffer's memory will be cleared by the destructor
1225 before de-allocating it (default is @c true upon instantiation).
1226
1227 @note
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
1234 @qualifier ADVANCED
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-
1240
1241 /*======================================================================*//**
1242 @brief
1243 Obtain block size in expansion policy.
1244 @returns Expansion policy's block size
1245 @see get_xp_max
1246 @see get_xp_play
1247 @see set_xp_block_size
1248 @qualifier ADVANCED
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-
1254
1255 /*======================================================================*//**
1256 @brief
1257 Obtain quantity of blocks available for use in buffer ring memory expansions.
1258 @returns Quantity of expansion blocks available
1259 @see get_available
1260 @see get_xp_block_size
1261 @see get_xp_blocks_utilized
1262 @see get_xp_max
1263 @see get_xp_play
1264 @qualifier ADVANCED
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-
1270
1271 /*======================================================================*//**
1272 @brief
1273 Obtain quantity of blocks utilized utilized for buffer ring memory expansion.
1274 @returns Quantity of expansion blocks utilized
1275 @see get_utilized
1276 @see get_xp_block_size
1277 @see get_xp_blocks_available
1278 @see get_xp_max
1279 @see get_xp_play
1280 @qualifier ADVANCED
1281 *///=========================================================================
1282 size_t get_xp_blocks_utilized() noexcept {
1283 std::lock_guard<std::mutex> guard(__mutex);
1284 return __xp_used;
1285 } // -x- size_t get_xp_blocks_utilized -x-
1286
1287 /*======================================================================*//**
1288 @brief
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
1294 @see get_xp_play
1295 @see set_xp_max
1296 @qualifier ADVANCED
1297 *///=========================================================================
1298 size_t get_xp_max() noexcept {
1299 std::lock_guard<std::mutex> guard(__mutex);
1300 return __xp_max;
1301 } // -x- size_t get_xp_max -x-
1302
1303 /*======================================================================*//**
1304 @brief
1305 Obtain minimum quantity of blocks available before automatic expansion block
1306 reduction policy.
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
1311 @see get_xp_max
1312 @see set_xp_play
1313 @qualifier ADVANCED
1314 *///=========================================================================
1315 size_t get_xp_play() noexcept {
1316 std::lock_guard<std::mutex> guard(__mutex);
1317 return __xp_play;
1318 } // -x- size_t get_xp_play -x-
1319
1320 /*======================================================================*//**
1321 @brief
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)
1328 @see c_str
1329 @see peek_to_array
1330 @see peek_to_string
1331 @see peek_to_vector
1332 @see remove
1333 *///=========================================================================
1334 char peek() {
1335 std::lock_guard<std::mutex> guard(__mutex);
1336
1337 // --------------------------------------------------------------------------
1338 // Syntax checks.
1339 // --------------------------------------------------------------------------
1340 if (__utilized() == 0) throw std::overflow_error("Ring buffer is empty");
1341
1342 // --------------------------------------------------------------------------
1343 // Duplicate the last byte of data from the ring buffer.
1344 // --------------------------------------------------------------------------
1345 return __buffer[__tail]; // Copy entry
1346
1347 } // -x- char peek -x-
1348
1349 /*======================================================================*//**
1350 @brief
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.
1353 @post
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)
1360 @see c_str
1361 @see peek
1362 @see peek_to_string
1363 @see peek_to_vector
1364 @see remove_to_array
1365 *///=========================================================================
1366 char* peek_to_array(
1367 /// Amount of data to duplicate
1368 const size_t len,
1369 /// Where to store the data@n
1370 /// @c nullptr = allocate the needed memory using @c malloc()
1371 char* data = nullptr) {
1372
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-
1380
1381 // --------------------------------------------------------------------------
1382 // Remove data from ring buffer.
1383 // --------------------------------------------------------------------------
1384 { std::lock_guard<std::mutex> guard(__mutex);
1385 size_t t = __tail;
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
1390 } // -x- for i -x-
1391 } // -x- mutex guard -x-
1392
1393 return data;
1394 } // -x- char* peek_to_array -x-
1395
1396 /*======================================================================*//**
1397 @brief
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)
1405 @see c_str
1406 @see peek
1407 @see peek_to_array
1408 @see peek_to_vector
1409 @see remove_to_string
1410 *///=========================================================================
1411 std::string peek_to_string(
1412 /// Amount of data to duplicate@n
1413 /// -1 = all data
1414 int len = -1,
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) {
1418
1419 // --------------------------------------------------------------------------
1420 // Syntax checks.
1421 // --------------------------------------------------------------------------
1422 if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
1423
1424 // --------------------------------------------------------------------------
1425 // Internal variables.
1426 // --------------------------------------------------------------------------
1427 std::string new_data;
1428
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 // --------------------------------------------------------------------------
1433 if (len != 0) {
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
1439 size_t t = __tail;
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
1444 } // -x- for i -x-
1445 } // -x- if len -x-
1446
1447 return new_data;
1448 } // -x- std::string peek_to_string -x-
1449
1450 /*======================================================================*//**
1451 @brief
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)
1460 @see c_str
1461 @see peek
1462 @see peek_to_array
1463 @see peek_to_string
1464 @see remove_to_vector
1465 *///=========================================================================
1466 std::vector<char> peek_to_vector(
1467 /// Amount of data to duplicate@n
1468 /// -1 = all data
1469 int len = -1,
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>()) {
1473
1474 // --------------------------------------------------------------------------
1475 // Syntax checks.
1476 // --------------------------------------------------------------------------
1477 if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
1478
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 // --------------------------------------------------------------------------
1483 if (len != 0) {
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
1488 size_t t = __tail;
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
1493 } // -x- for i -x-
1494 } // -x- if len -x-
1495
1496 return data;
1497 } // -x- std::vector<char> peek_to_vector -x-
1498
1499 /*======================================================================*//**
1500 @brief
1501 Remove one character from the utilized portion of the internal ring buffer,
1502 and return it .
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)
1507 @see c_str
1508 @see peek
1509 @see remove_to_array
1510 @see remove_to_string
1511 @see remove_to_vector
1512 *///=========================================================================
1513 char remove() {
1514
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 // --------------------------------------------------------------------------
1519 char ch;
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-
1526
1527 // --------------------------------------------------------------------------
1528 // Ring buffer memory allocation maintenance.
1529 // --------------------------------------------------------------------------
1530 __adjust_xp();
1531
1532 return ch;
1533 } // -x- char remove -x-
1534
1535 /*======================================================================*//**
1536 @brief
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.
1539 @post
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)
1546 @see c_str
1547 @see peek_to_array
1548 @see remove
1549 @see remove_to_string
1550 @see remove_to_vector
1551 *///=========================================================================
1552 char* remove_to_array(
1553 /// Maximum quantity of data to move@n
1554 const size_t len,
1555 /// Where to store the data@n
1556 /// @c nullptr = allocate the needed memory using @c malloc()
1557 char* data = nullptr) {
1558
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-
1566
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
1576 } // -x- for i -x-
1577 } // -x- mutex guard -x-
1578
1579 // --------------------------------------------------------------------------
1580 // Ring buffer memory allocation maintenance.
1581 // --------------------------------------------------------------------------
1582 __adjust_xp();
1583
1584 return data;
1585 } // -x- char* remove_to_array -x-
1586
1587 /*======================================================================*//**
1588 @brief
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)
1596 @see c_str
1597 @see peek_to_string
1598 @see remove
1599 @see remove_to_array
1600 @see remove_to_vector
1601 *///=========================================================================
1602 std::string remove_to_string(
1603 /// Amount of data to remove@n
1604 /// -1 = all data
1605 int len = -1,
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) {
1609
1610 // --------------------------------------------------------------------------
1611 // Syntax checks.
1612 // --------------------------------------------------------------------------
1613 if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
1614
1615 // --------------------------------------------------------------------------
1616 // Internal variables.
1617 // --------------------------------------------------------------------------
1618 std::string new_data;
1619
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 // --------------------------------------------------------------------------
1624 if (len > 0) {
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
1634 } // -x- for i -x-
1635 } // -x- if len -x-
1636
1637 // --------------------------------------------------------------------------
1638 // Ring buffer memory allocation maintenance.
1639 // --------------------------------------------------------------------------
1640 __adjust_xp();
1641
1642 return new_data;
1643 } // -x- std::string remove_to_string -x-
1644
1645 /*======================================================================*//**
1646 @brief
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)
1655 @see c_str
1656 @see peek_to_vector
1657 @see remove
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
1663 /// -1 = all data
1664 int len = -1,
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>()) {
1668
1669 // --------------------------------------------------------------------------
1670 // Syntax checks.
1671 // --------------------------------------------------------------------------
1672 if (len < -1) throw std::invalid_argument("Unrealistic data length of " + std::to_string(len));
1673
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 // --------------------------------------------------------------------------
1678 if (len > 0) {
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
1687 } // -x- for i -x-
1688 } // -x- if len -x-
1689
1690 // --------------------------------------------------------------------------
1691 // Ring buffer memory allocation maintenance.
1692 // --------------------------------------------------------------------------
1693 __adjust_xp();
1694
1695 return data;
1696 } // -x- std::vector<char> remove_to_vector -x-
1697
1698 /*======================================================================*//**
1699 @brief
1700 Flip the order of all entries in the ring buffer.
1701 @returns The same rring object so as to facilitate stacking
1702 *///=========================================================================
1703 rring& reverse() {
1704 char carry;
1705 { std::lock_guard<std::mutex> guard(__mutex);
1706 int h = __head;
1707 int t = __tail;
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
1715 } // -x- for i -x-
1716 } // -x- mutex guard -x-
1717 return *this;
1718 } // -x- rring& reverse -x-
1719
1720 /*======================================================================*//**
1721 @brief
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
1728 @see get_available
1729 @see get_head
1730 @see get_tail
1731 @see get_utilized
1732 @see set_tail
1733 @qualifier ADVANCED
1734 *///=========================================================================
1735 rring& set_head(
1736 /// Position adjustment (may also be negative, but only for relative @c type)
1737 const int offset,
1738 /// Type of adjustment@n
1739 /// TRUE = absolute (default)@n
1740 /// FALSE = relative (@c offset may also be negative)
1741 const bool type = true) {
1742
1743 // --------------------------------------------------------------------------
1744 // Absolute position.
1745 // --------------------------------------------------------------------------
1746 if (type) {
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;
1751
1752 // --------------------------------------------------------------------------
1753 // Relative position.
1754 // --------------------------------------------------------------------------
1755 } else {
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");
1763 __head += offset;
1764 if (__head >= __size) __head += __size;
1765 } // -x- if offset -x-
1766 } // -x- if type -x-
1767
1768 return *this;
1769 } // -x- rring& set_head -x-
1770
1771 /*======================================================================*//**
1772 @brief
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
1779 @see get_available
1780 @see get_size
1781 @see get_utilized
1782 *///=========================================================================
1783 rring& set_size(
1784 /// New size of ring buffer
1785 const size_t len) {
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.
1791 __size = len;
1792 } // -x- mutex guard -x-
1793
1794 return *this;
1795 } // -x- rring& set_size -x-
1796
1797 /*======================================================================*//**
1798 @brief
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
1805 @see get_available
1806 @see get_head
1807 @see get_tail
1808 @see get_utilized
1809 @see set_head
1810 @qualifier ADVANCED
1811 *///=========================================================================
1812 rring& set_tail(
1813 /// Position adjustment (may also be negative, but only for relative @c type)
1814 const int offset,
1815 /// Type of adjustment@n
1816 /// TRUE = absolute (default)@n
1817 /// FALSE = relative (@c offset may also be negative)
1818 const bool type = true) {
1819
1820 // --------------------------------------------------------------------------
1821 // Absolute position.
1822 // --------------------------------------------------------------------------
1823 if (type) {
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;
1828
1829 // --------------------------------------------------------------------------
1830 // Relative position.
1831 // --------------------------------------------------------------------------
1832 } else {
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");
1840 __tail += offset;
1841 if (__tail >= __size) __tail += __size;
1842 } // -x- if offset -x-
1843 } // -x- if type -x-
1844
1845 return *this;
1846 } // -x- rring& set_tail -x-
1847
1848 /*======================================================================*//**
1849 @brief
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).
1852
1853 @note
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
1860 @qualifier ADVANCED
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-
1869
1870 return *this;
1871 } // -x- rring& set_wipe_policy -x-
1872
1873 /*======================================================================*//**
1874 @brief
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
1882 @see set_xp_max
1883 @see set_xp_play
1884 @qualifier ADVANCED
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
1892
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;
1901
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");
1906
1907 // --------------------------------------------------------------------------
1908 // Update __xp_block_size and then call __adjust_xp to optimize memory block
1909 // re-allocation.
1910 // --------------------------------------------------------------------------
1911 __xp_block_size = xp_block_size;
1912 __adjust_xp(base_size + new_xp_used_entries);
1913
1914 } // -x- mutex guard -x-
1915
1916 return *this;
1917 } // -x- rring& set_xp_block_size -x-
1918
1919 /*======================================================================*//**
1920 @brief
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
1927 @see get_xp_max
1928 @see set_xp_block_size
1929 @see set_xp_play
1930 @qualifier ADVANCED
1931 *///=========================================================================
1932 rring& set_xp_max(
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);
1937
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) {
1943 __xp_max = xp_max;
1944
1945 // --------------------------------------------------------------------------
1946 // Decreasing the maximum requires making sure we don't lose data.
1947 // --------------------------------------------------------------------------
1948 } else {
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");
1953 __xp_max = xp_max;
1954 } // -x- if xp_max -x-
1955 } // -x- mutex guard -x-
1956
1957 return *this;
1958 } // -x- rring& set_xp_max -x-
1959
1960 /*======================================================================*//**
1961 @brief
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
1965 @see get_xp_play
1966 @see set_xp_block_size
1967 @see set_xp_max
1968 @qualifier ADVANCED
1969 *///=========================================================================
1970 rring& set_xp_play(
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-
1976
1977 return *this;
1978 } // -x- rring& set_xp_play -x-
1979
1980 /*======================================================================*//**
1981 @brief
1982 Convert the entire contents of this ring buffer to an ASCIIZ string in the
1983 form of a @c char* array.
1984 @post
1985 The string returned will need to be `free()`'d after it's no longer needed.
1986
1987 @code{.cpp}
1988 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1989 #include <randolf/rring>
1990
1991 int main(int argc, char *argv[]) {
1992 randolf::rring rb(1024);
1993 rb.append("This is an example.");
1994 char* c = rb;
1995 std::cout << "\"" << c << "\"" << std::endl;
1996 ::free(c);
1997 return EXIT_SUCCESS;
1998 } // -x- int main -x-
1999 @endcode
2000
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.
2003 @see c_str
2004 *///=========================================================================
2005 operator char*() noexcept {
2006 return c_str();
2007 } // -x- operator char*() -x-
2008
2009 /*======================================================================*//**
2010 @brief
2011 Convert the entire contents of this ring buffer to an @c std::string object.
2012
2013 @code{.cpp}
2014 #include <iostream> // std::cout, std::cerr, std::endl, etc.
2015 #include <string> // std::string
2016 #include <randolf/rring>
2017
2018 int main(int argc, char *argv[]) {
2019 randolf::rring rb(1024);
2020 rb.append("This is an example.");
2021 std::string s = rb;
2022 std::cout << "\"" << s << "\"" << std::endl;
2023 return EXIT_SUCCESS;
2024 } // -x- int main -x-
2025 @endcode
2026
2027 @returns Contents of this ring buffer as an std::string object
2028 @see peek_to_string
2029 *///=========================================================================
2030 operator std::string() noexcept {
2031 return peek_to_string();
2032 } // -x- operator std::string -x-
2033
2034 /*======================================================================*//**
2035 @brief
2036 Convert the entire contents of this ring buffer to an @c std::vector<char>
2037 object.
2038
2039 @code{.cpp}
2040 #include <iostream> // std::cout, std::cerr, std::endl, etc.
2041 #include <string> // std::string
2042 #include <vector> // std::vector
2043 #include <randolf/rring>
2044
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-
2052 @endcode
2053
2054 @returns Contents of this ring buffer as an std::vector<char> object
2055 @see peek_to_vector
2056 *///=========================================================================
2057 operator std::vector<char>() noexcept {
2058 return peek_to_vector();
2059 } // -x- operator std::vector<char> -x-
2060
2061 /*======================================================================*//**
2062 @brief
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.
2065
2066 The first element is at index 0.
2067 @throws std::out_of_range if the index is out-of-range
2068 @returns char
2069 @see at
2070 *///=========================================================================
2071 char operator[](
2072 /// Index of character to access (0 = first element; negative index values
2073 /// are calculated in reverse, starting with -1 as the final position)
2074 int index) {
2075 std::lock_guard<std::mutex> guard(__mutex);
2076
2077 // --------------------------------------------------------------------------
2078 // Internal variables.
2079 // --------------------------------------------------------------------------
2080 int MAX = __utilized();
2081
2082 // --------------------------------------------------------------------------
2083 // Syntax checks.
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");
2086
2087 // --------------------------------------------------------------------------
2088 // Calculate character's position.
2089 // --------------------------------------------------------------------------
2090 index = index >= 0 ? __tail + index : __head + index;
2091 if (index >= __size) index -= __size;
2092
2093 return __buffer[index];
2094 } // -x- char operator[] -x-
2095
2096 /*======================================================================*//**
2097 @brief
2098 Support convenient streaming usage with std::cout, std::cerr, and friends.
2099
2100 @code{.cpp}
2101 #include <iostream> // std::cout, std::cerr, std::endl, etc.
2102 #include <string> // std::string
2103 #include <randolf/rring>
2104
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-
2111 @endcode
2112
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
2115 of scope)
2116 @see peek_to_string
2117 *///=========================================================================
2118 friend std::ostream& operator<< (
2119 /// Output stream (provided automatically by std::cout and std::cerr)
2120 std::ostream& o,
2121 /// Object class (matched by compiler)
2122 rring& c) noexcept {
2123 return o << c.peek_to_string();
2124 } // -x- std::ostream& operator<< -x-
2125
2126 }; // -x- class rring -x-
2127
2128}; // -x- namespace randolf -x-