Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ringfilecache.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
31
77# ifndef RINGFILECACHE_H
78# define RINGFILECACHE_H
79
80# include <sys/time.h>
81# include <stdio.h>
82# include <cassert>
83# include <string.h>
84# include <stdexcept>
85# include <memory>
86# include <fstream>
87# include <iostream>
88# include <string>
89# include <sstream>
90# include <type_traits>
91
92# include <ah-errors.H>
93# include <tpl_array.H>
94
96inline constexpr size_t Ring_Max_Name_Size = 4096;
97
129template <typename T>
131{
132public:
133 static_assert(std::is_trivially_copyable<T>::value,
134 "RingFileCache requires trivially copyable types");
135 static_assert(std::is_default_constructible<T>::value,
136 "T must be default constructible");
137
138 // The cache parameters are stored in a binary file with this structure
139 struct Pars
140 {
141 size_t dim; // capacity in entries of cache
142 size_t n; // Number of entries stored in cache
143 size_t head; // next integral position ready for reading
144 size_t tail; // next integral position ready for writing
145 size_t size_cache_file = 0; // num of chars of std::string (including '\0')
146 char cache_file_name[0]; // be careful with this field. DON'T USE IT!
147
148 [[nodiscard]] std::string to_string() const
149 {
150 std::ostringstream s;
151 s << dim << " " << n << " " << tail << " " << head << " "
153 return s.str();
154 }
155
156 friend std::ostream &operator <<(std::ostream & out, const Pars & pars)
157 {
158 return out << pars.to_string();
159 }
160 };
161
162private:
163 bool initialized = false;
164 std::string pars_file_name;
165 std::string cache_file_name;
166
167 std::fstream pars_stream; // the stream for the pars (read/write mode)
168 std::fstream cache_stream; // the stream for the cache (read/write mode)
169
170 size_t dim = 0; // the maximum number of entries
171 size_t n = 0; // the current number of stored entries
172 size_t head = 0; // next integral position ready for reading
173 size_t tail = 0; // next integral position ready for writing
174
177 {
178 cache_stream.seekp(tail * sizeof(T));
179 }
180
182 bool is_valid_offset(const size_t offset) const noexcept
183 {
184 return offset < n;
185 }
186
188 bool is_valid_position(const size_t pos) const noexcept
189 {
190 if (head <= tail)
191 return pos >= head and pos < tail;
192 // Wraparound case: valid if pos >= head OR pos < tail
193 return pos >= head or pos < tail;
194 }
195
198 {
199 const size_t pos = (head + num_entries) % dim;
200 cache_stream.seekg(pos * sizeof(T));
201 }
202
205 {
206 T ret;
207 cache_stream.read(reinterpret_cast<char *>(&ret), sizeof ret);
208
209 return ret;
210 }
211
213 T read_entry(const size_t pos)
214 {
215 ah_range_error_if(pos >= n)
216 << "read_entry(" << pos << "): cache has " << n << " items";
217
218 const size_t real_pos = (head + pos) % dim;
219
220 T ret;
221 cache_stream.seekg(real_pos * sizeof(ret));
222 cache_stream.read(reinterpret_cast<char *>(&ret), sizeof ret);
223 return ret;
224 }
225
227 void write_entry(const T & item)
228 {
229 cache_stream.write(reinterpret_cast<const char *>(&item), sizeof item);
230 }
231
233 void validate_absolute_position(const size_t pos) const
234 {
235 if (pos < dim)
236 return;
237
238 ah_out_of_range_error() << "position " << pos << " is greater than dim " << dim;
239 }
240
242 T read_absolute(const size_t pos)
243 {
245 T ret;
246 cache_stream.seekg(pos * sizeof(T));
247 cache_stream.read(reinterpret_cast<char *>(&ret), sizeof ret);
248 return ret;
249 }
250
252 void write_absolute(const size_t pos, const T & item)
253 {
255 cache_stream.seekp(pos * sizeof(T));
256 cache_stream.write(reinterpret_cast<const char *>(&item), sizeof item);
257 }
258
259public:
261 std::string to_string() const
262 {
263 const size_t tg = const_cast<std::fstream &>(cache_stream).tellg() / sizeof(T);
264 const size_t tp = const_cast<std::fstream &>(cache_stream).tellp() / sizeof(T);
265 std::ostringstream s;
266 s << "Cache pars" << std::endl
267 << "capacity = " << capacity() << std::endl
268 << "size = " << size() << std::endl
269 << "sizeof T = " << sizeof(T) << std::endl
270 << "head pos = " << head_pos() << std::endl
271 << "tail pos = " << tail_pos() << std::endl
272 << "tellg/T = " << tg << std::endl
273 << "tellp/T = " << tp << std::endl;
274 return s.str();
275 }
276
277 friend std::ostream &operator <<(std::ostream & out, const RingFileCache & cache)
278 {
279 return out << cache.to_string();
280 }
281
303 static void create(const std::string & pars_file_name,
304 const std::string & cache_file_name,
305 const size_t num_entries)
306 {
307 std::ostringstream s;
308
310 << "cache_file_name too long (max " << (Ring_Max_Name_Size - 1) << " chars)";
311
312 std::fstream pars_stream(pars_file_name, std::ios::binary | std::ios::out);
314 << "cannot open " << pars_file_name;
315
316 std::fstream cache_stream(cache_file_name, std::ios::binary | std::ios::out);
318 << "cannot open " << cache_file_name;
319
320 const size_t pars_size = sizeof(Pars) + cache_file_name.size() + 1;
321 std::shared_ptr<Pars> pars_ptr(static_cast<Pars *>(malloc(pars_size)), free);
322
323 ah_bad_alloc_if(pars_ptr == nullptr);
324
325 pars_ptr->dim = num_entries;
326 pars_ptr->n = pars_ptr->head = pars_ptr->tail = 0;
327 pars_ptr->size_cache_file = cache_file_name.size() + 1;
328 strncpy(pars_ptr->cache_file_name, cache_file_name.c_str(),
329 pars_ptr->size_cache_file);
330 pars_ptr->cache_file_name[pars_ptr->size_cache_file - 1] = '\0';
331 pars_stream.write(reinterpret_cast<const char *>(pars_ptr.get()), pars_size);
332
333 T init{};
334 for (size_t i = 0; i < num_entries; ++i)
335 cache_stream.write(reinterpret_cast<const char *>(&init), sizeof(init));
336 }
337
338private:
339 static std::string state(std::fstream & ss)
340 {
341 std::ostringstream s;
342 const auto state = ss.rdstate();
343 if (state & std::ios::goodbit)
344 s << " goodbit = true " << std::endl;
345 else
346 s << " goodbit = false " << std::endl;
347 if (state & std::ios::badbit)
348 s << " badbit = true " << std::endl;
349 else
350 s << " badbit = false " << std::endl;
351 if (state & std::ios::failbit)
352 s << " failbit = true " << std::endl;
353 else
354 s << " failbit = false " << std::endl;
355 if (state & std::ios::eofbit)
356 s << " eofbit = true " << std::endl;
357 else
358 s << " eofbit = false " << std::endl;
359 return s.str();
360 }
361
362 // Helper method for initializing the cache from the pars file
363 void read_pars(const std::string & pars_file_name)
364 {
365 pars_stream.open(pars_file_name, std::ios::binary | std::ios::out | std::ios::in);
366
367 ah_domain_error_if((not pars_stream.is_open()) or (not pars_stream.good()))
368 << "cannot open " << pars_file_name;
369
370 // set the cache internal state from the saved state in pars file
371 Pars pars;
372 pars_stream.read(reinterpret_cast<char *>(&pars), sizeof pars);
373 dim = pars.dim;
374 n = pars.n;
375 head = pars.head;
376 tail = pars.tail;
377
378 // read the cache file name, which should be saved at the end of
379 // pars file
380 const size_t name_size = std::min(Ring_Max_Name_Size, pars.size_cache_file);
381 const std::shared_ptr<char> name(static_cast<char *>(malloc(name_size)), free);
382 pars_stream.read(name.get(), name_size);
383 cache_file_name = name.get();
384
385 cache_stream.open(cache_file_name, std::ios::binary | std::ios::out | std::ios::in);
387 << "cannot open " << cache_file_name;
388
389 pars_stream.seekp(0);
390
391 cache_stream.seekg(head * sizeof(T)); // set read position
392 cache_stream.seekp(tail * sizeof(T)); // set write position
393 initialized = true;
394
395 assert(pars_stream.good());
396 assert(cache_stream.good());
397 }
398
399public:
406 {
407 friend class RingFileCache;
408
410 size_t pos = 0; // this an absolute position in the file
411
412 void validate_position(const size_t pos) const
413 {
415 return;
416
417 std::ostringstream s;
418 s << "position " << pos << " is outside of maximum " << cache_ptr->dim;
420 }
421
422 void increase_pos(const long delta = 1) noexcept
423 {
424 if (delta < 0)
425 {
426 decrease_pos(-delta);
427 return;
428 }
429
430 const size_t dim = cache_ptr->dim;
431 pos = (pos + static_cast<size_t>(delta)) % dim;
432 }
433
434 void decrease_pos(const long delta = 1) noexcept
435 {
436 if (delta < 0)
437 {
438 increase_pos(-delta);
439 return;
440 }
441
442 const size_t dim = cache_ptr->dim;
443 if (const size_t decr = static_cast<size_t>(delta) % dim; pos >= decr)
444 pos -= decr;
445 else
446 pos = dim - (decr - pos);
447 }
448
449 public:
450 Pointer() = default;
451
457 Pointer(const RingFileCache & cache, const size_t __pos = 0)
458 : cache_ptr(&const_cast<RingFileCache &>(cache)),
459 pos((cache.head_pos() + __pos) % cache.dim) {}
460
463 {
464 increase_pos(1);
465 return *this;
466 }
467
469 Pointer operator ++(int) noexcept
470 {
471 Pointer ret = *this;
472 increase_pos(1);
473 return ret;
474 }
475
478 {
479 decrease_pos(1);
480 return *this;
481 }
482
484 Pointer operator --(int) noexcept
485 {
486 Pointer ret = *this;
487 decrease_pos(1);
488 return ret;
489 }
490
492 Pointer &operator +=(const long val) noexcept
493 {
494 increase_pos(val);
495 return *this;
496 }
497
499 Pointer &operator -=(const long val) noexcept
500 {
501 decrease_pos(val);
502 return *this;
503 }
504
506 Pointer operator +(const long val) const noexcept
507 {
508 Pointer ret = *this;
509 ret += val;
510 return ret;
511 }
512
514 Pointer operator -(const long val) const noexcept
515 {
516 Pointer ret = *this;
517 ret -= val;
518 return ret;
519 }
520
523 {
524 auto head = cache_ptr->head;
525 if (pos >= head)
526 return pos - head;
527 return cache_ptr->dim - head + pos;
528 }
529
532 {
534 }
535 };
536
538 T read(const Pointer & ptr)
539 {
540 ah_domain_error_if(ptr.cache_ptr != this)
541 << "RingFileCache::read(const Pointer&): invalid ptr";
542
543 return read_absolute(ptr.pos);
544 }
545
547 void write(const Pointer & ptr, const T & item)
548 {
549 ah_domain_error_if(ptr.cache_ptr != this)
550 << "RingFileCache::write(const Pointer&): invalid ptr";
551
552 return write_absolute(ptr.pos, item);
553 }
554
556 bool is_initialized() const { return initialized; }
557
559 size_t size() const noexcept { return n; }
560
562 size_t capacity() const noexcept { return dim; }
563
565 size_t avail() const noexcept { return dim - n; }
566
568 size_t head_pos() const noexcept { return head; }
569
571 size_t tail_pos() const noexcept { return tail; }
572
574 bool is_empty() const noexcept { return n == 0; }
575
577 bool is_full() const noexcept { return n == dim; }
578
597
607 RingFileCache() = default;
608
615 static bool test(const std::string & pars_fname)
616 {
617 std::ifstream pars_stream_test(pars_fname, std::ios::binary);
618 if (not pars_stream_test.good())
619 return false;
620
621 Pars pars;
622 pars_stream_test.read(reinterpret_cast<char *>(&pars), sizeof pars);
623
624 // read the cache file name, which should be saved at the end of
625 // pars file
626 const size_t name_size = std::min(Ring_Max_Name_Size, pars.size_cache_file);
627 std::shared_ptr<char> name(static_cast<char *>(malloc(name_size)), free);
628 pars_stream_test.read(name.get(), name_size);
629 const char *cache_file_name = name.get();
630
631 if (std::ifstream cache_stream(cache_file_name, std::ios::binary); not cache_stream.good())
632 return false;
633
634 return true;
635 }
636
646 void init(const std::string & pars_fname)
647 {
649 << "this cache has already an opened pars file";
650
653 }
654
665 bool put(const T & item)
666 {
667 if (n == dim) // cache full?
668 return false;
669
670 // assert(cache_stream.good());
671
673
674 assert(cache_stream.tellp() == tail*sizeof(T));
675
676 write_entry(item);
677 if (++tail == dim) // eof border reached?
678 {
679 tail = 0;
680 cache_stream.seekp(0);
681 assert(cache_stream.good());
682 }
683 ++n;
684
685 assert(cache_stream.good());
686 assert(cache_stream.tellp() == tail*sizeof(T));
687
688 return true;
689 }
690
712 bool read(T entries[], const size_t m = 1)
713 {
714 if (m > n)
715 return false;
716
718
719 assert(cache_stream.good());
720 // assert(cache_stream.tellg() == head*sizeof(T));
721
722 const size_t n_avail_until_eof = dim - head;
723 if (m <= n_avail_until_eof)
724 cache_stream.read(reinterpret_cast<char *>(entries), m * sizeof(T));
725 else
726 {
727 cache_stream.read(reinterpret_cast<char *>(entries),
728 n_avail_until_eof * sizeof(T));
729 cache_stream.seekg(0);
730 cache_stream.read(reinterpret_cast<char *>(&entries[n_avail_until_eof]),
731 (m - n_avail_until_eof) * sizeof(T));
732 assert(cache_stream.gcount() == (m - n_avail_until_eof)*sizeof(T));
733 }
734
735 assert(cache_stream.good());
736
737 return true;
738 }
739
746 {
747 ah_underflow_error_if(n == 0) << "read_first(): cache is empty";
748
750 T ret_val;
751 read(&ret_val);
752 return ret_val;
753 }
754
761 {
762 ah_underflow_error_if(n == 0) << "read_last(): cache is empty";
763
764 if (tail == 0)
765 cache_stream.seekg((dim - 1) * sizeof(T));
766 else
767 cache_stream.seekg((tail - 1) * sizeof(T));
768
769 return read_entry();
770 }
771
777 T youngest() { return read_last(); }
778
784 T oldest() { return read_first(); }
785
792 T oldest(size_t i)
793 {
795 << "oldest(" << i << ") but the cache has " << n << " entries";
796
797 return read_entry(i);
798 }
799
806 {
807 if (n == 0)
808 return Array<T>();
809
810 Array<T> ret(n);
811 ret.putn(n);
812 assert(ret.capacity() >= n);
813 read(&ret.base(), n);
814 return ret;
815 }
816
822 Array<T> read_from(const size_t pos, const size_t m)
823 {
825 Iterator it(*this, pos);
826 for (size_t i = 0; i < m and it.has_curr(); ++i, it.next_ne())
827 ret.append(it.get_curr());
828
829 return ret;
830 }
831
837 Array<T> read_from(const Pointer & ptr, const size_t m)
838 {
840 Iterator it(*this, ptr.get_pos_respect_to_head());
841 for (size_t i = 0; i < m and it.has_curr(); ++i, it.next_ne())
842 ret.append(it.get_curr_ne());
843
844 return ret;
845 }
846
852 bool get(const size_t m = 1) noexcept
853 {
854 if (m > n)
855 return false;
856
857 head = (head + m) % dim;
858 n -= m;
859
860 return true;
861 }
862
868 void empty() noexcept { get(n); }
869
876 void flush()
877 {
878 assert(pars_stream.tellp() == 0);
879 Pars pars;
880 pars.dim = dim;
881 pars.n = n;
882 pars.head = head;
883 pars.tail = tail;
884 pars.size_cache_file = cache_file_name.size() + 1;
885 pars_stream.write(reinterpret_cast<const char *>(&pars), sizeof(pars));
886 pars_stream.flush();
887 pars_stream.seekp(0);
888
889 cache_stream.flush();
890 }
891
897 void close()
898 {
899 if (not initialized)
900 return;
901 flush();
902 cache_stream.close();
903 pars_stream.close();
904 initialized = false;
905 }
906
908
918 void resize(const size_t sz)
919 {
921 << "RingFileCache::resize(): file truncation is not implemented (yet?)";
922
923 if (sz == dim)
924 return; // no-op
925
926 const size_t old_dim = dim;
927
928 // Handle wraparound case: data spans [head, old_dim) and [0, tail)
929 // Move the wrapped portion [0, tail) to [old_dim, old_dim + tail)
930 if (n > 0 && tail <= head && tail > 0)
931 {
932 const size_t wrapped_count = tail;
933 auto wrapped_data = std::make_unique<T[]>(wrapped_count);
934
935 cache_stream.seekg(0);
936 cache_stream.read(reinterpret_cast<char *>(wrapped_data.get()),
937 wrapped_count * sizeof(T));
938
939 cache_stream.seekp(old_dim * sizeof(T));
940 cache_stream.write(reinterpret_cast<const char *>(wrapped_data.get()),
941 wrapped_count * sizeof(T));
942
944 }
945
946 // Zero-initialize remaining slots from current tail (or old_dim) to sz
947 T init{};
948 const size_t fill_start = (tail > old_dim) ? tail : old_dim;
949 for (size_t pos = fill_start; pos < sz; ++pos)
950 {
951 cache_stream.seekp(pos * sizeof(T));
953 }
954
955 dim = sz;
956 // tail is already correct after wraparound fix, or needs update for non-wrapped case
957 if (n > 0 && tail <= old_dim)
958 tail = (head + n) % dim;
959
961 flush();
962 }
963
964 // Iterator for read-only traversal (oldest to youngest).
965 // Not reentrant: do not execute other operations on the cache object
966 // while iterating.
968 {
971 size_t pos = 0;
972 size_t curr_pos = 0;
973
975 {
976 cache_ptr->cache_stream.seekg(curr_pos * sizeof(T));
977 }
978
980 {
981 ++pos;
982 if (++curr_pos == cache_ptr->dim)
983 curr_pos = 0;
984 }
985
987 {
989 curr = cache_ptr->read_entry();
990 }
991
992 public:
994 [[nodiscard]] size_t get_pos() const noexcept { return pos; }
995
997 [[nodiscard]] bool has_curr() const { return pos < cache_ptr->n; }
998
1005 Iterator(const RingFileCache<T> & cache, const size_t offset = 0)
1006 : cache_ptr(&const_cast<RingFileCache<T> &>(cache)),
1007 pos(offset), curr_pos((cache_ptr->head + offset) % cache.dim)
1008 {
1009 if (cache_ptr->is_empty())
1010 return;
1011
1012 if (not cache_ptr->is_valid_offset(pos))
1013 return;
1014
1015 load_curr();
1016 }
1017
1020
1026 T get_curr() const
1027 {
1029 << "RingFileCache::Iterator::get_curr(): iterator exhausted";
1030 return get_curr_ne();
1031 }
1032
1035 {
1036 increase_pos();
1037 if (has_curr())
1038 load_curr();
1039 }
1040
1045 void next()
1046 {
1048 << "RingFileCache::Iterator::next(): iterator exhausted";
1049 next_ne();
1050 }
1051 };
1052
1057 Iterator get_it() { return Iterator(*this); }
1058};
1059
1060# endif // RINGFILECACHE_H
Exception handling system with formatted messages for Aleph-w.
#define ah_out_of_range_error_if(C)
Throws std::out_of_range if condition holds.
Definition ah-errors.H:579
#define ah_underflow_error_if(C)
Throws std::underflow_error if condition holds.
Definition ah-errors.H:368
#define ah_overflow_error_if(C)
Throws std::overflow_error if condition holds.
Definition ah-errors.H:463
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:522
#define ah_bad_alloc_if(C)
Throws std::bad_alloc if condition holds.
Definition ah-errors.H:429
#define ah_range_error_if(C)
Throws std::range_error if condition holds.
Definition ah-errors.H:207
#define ah_out_of_range_error()
Throws std::out_of_range unconditionally.
Definition ah-errors.H:611
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
T & append(const T &item)
Append a new item by copy.
Definition htlist.H:1562
Slinknc * head
Definition htlist.H:508
Slinknc * tail
Definition htlist.H:509
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
LocateFunctions< Container, Type > * base() const
Definition ah-dry.H:172
Iterator(const RingFileCache< T > &cache, const size_t offset=0)
Construct an iterator positioned offset entries forward from the oldest cache entry.
RingFileCache< T > * cache_ptr
T get_curr_ne() const noexcept
Return the current element (no bounds check). Undefined if exhausted.
bool has_curr() const
True if the iterator currently refers to a valid element.
void next()
Advance to the next element.
size_t get_pos() const noexcept
Logical offset from the head (number of elements already visited)
T get_curr() const
Return the current element.
void next_ne() noexcept
Advance to the next element without checking bounds. Safe to call when exhausted.
Defines a pointer to a specific location in the cache.
size_t get_pos() const noexcept
Alias of get_pos_respect_to_head()
RingFileCache * cache_ptr
Pointer operator--() noexcept
Prefix decrement: move back one position (with wraparound)
Pointer operator++() noexcept
Prefix increment: advance one position (with wraparound)
Pointer & operator-=(const long val) noexcept
Move back by val positions (with wraparound)
size_t get_pos_respect_to_head() const noexcept
Offset of this pointer relative to the current head of the cache.
void decrease_pos(const long delta=1) noexcept
Pointer(const RingFileCache &cache, const size_t __pos=0)
Construct a pointer to the current head (oldest item in the cache)
void validate_position(const size_t pos) const
Pointer operator+(const long val) const noexcept
Return a new Pointer advanced by val positions.
void increase_pos(const long delta=1) noexcept
Pointer & operator+=(const long val) noexcept
Advance by val positions (with wraparound)
Pointer operator-(const long val) const noexcept
Return a new Pointer moved back by val positions.
Persistent ring buffer cache with file-backed storage.
T read(const Pointer &ptr)
Read the entry pointed to by ptr
std::string pars_file_name
T oldest(size_t i)
Read the i-th oldest element without removing it.
T read_first()
Read the oldest entry in the cache.
RingFileCache(const std::string &pars_fname)
Initialize a cache previously built.
Array< T > read_all()
Read all entries in insertion order.
bool is_empty() const noexcept
Returns true if the cache is empty.
Array< T > read_from(const Pointer &ptr, const size_t m)
Read up to m entries starting from the position designated by ptr.
Iterator get_it()
Returns a forward iterator starting at the oldest entry.
void test_and_set_head_pointer(const size_t num_entries=0)
Position the read pointer at head + num_entries (with wraparound)
T read_entry(const size_t pos)
Read the entry at logical position pos from head (with bounds check)
bool put(const T &item)
Insert an item into the cache.
void init(const std::string &pars_fname)
Initialize a cache constructed with the default constructor.
size_t size() const noexcept
Returns the number of entries stored in the cache.
void test_and_set_tail_pointer()
Position the writing pointer at the current tail.
void empty() noexcept
Empties the cache; all entries are logically deleted.
size_t capacity() const noexcept
Returns the maximum capacity.
RingFileCache()=default
Default constructor.
bool is_full() const noexcept
Returns true if the cache is full (no more entries can be inserted)
std::fstream cache_stream
bool is_initialized() const
True if the cache has been initialized with a valid parameters file.
T read_entry()
Load an entry from the current stream position. WARNING: stream pointer not set here.
T youngest()
Alias for read_last().
bool read(T entries[], const size_t m=1)
Read the m the oldest entries and load them in a contiguous array.
static std::string state(std::fstream &ss)
Array< T > read_from(const size_t pos, const size_t m)
Read up to m entries starting from the pos-th oldest position.
std::fstream pars_stream
bool get(const size_t m=1) noexcept
Extracts (deletes) from the cache the m oldest inserted items.
std::string to_string() const
Human-readable dump of the cache internal state (debugging)
void flush()
Flushes the current cache state to disk.
bool is_valid_offset(const size_t offset) const noexcept
True if offset is a valid logical offset from the head (0 <= offset < n)
void close()
Flushes state and closes the underlying streams.
size_t head_pos() const noexcept
Returns the current head position.
std::string cache_file_name
bool is_valid_position(const size_t pos) const noexcept
True if pos is a valid absolute position within the stored entries.
static void create(const std::string &pars_file_name, const std::string &cache_file_name, const size_t num_entries)
Create a brand new on-disk ring cache.
void validate_absolute_position(const size_t pos) const
Throw out_of_range if pos >= dim.
T read_absolute(const size_t pos)
Read entry at absolute file position (with bounds check)
static bool test(const std::string &pars_fname)
Tests if pars and cache files have been created.
void resize(const size_t sz)
Resize the maximum capacity of the cache.
size_t avail() const noexcept
Returns the number of available entries.
void read_pars(const std::string &pars_file_name)
void write(const Pointer &ptr, const T &item)
Write item at an absolute position designated by ptr.
friend std::ostream & operator<<(std::ostream &out, const RingFileCache &cache)
T oldest()
Alias for read_first().
void write_entry(const T &item)
Write an entry at the current stream position.
size_t tail_pos() const noexcept
return the current tail position
void write_absolute(const size_t pos, const T &item)
Write entry at absolute file position (with bounds check)
T read_last()
Read the youngest (most recently inserted) entry in the cache.
const long double offset[]
Offset values indexed by symbol string length (bounded by MAX_OFFSET_INDEX)
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:107
static bool init
Definition hash-fct.C:47
DynList< T > maps(const C &c, Op op)
Classic map operation.
constexpr size_t Ring_Max_Name_Size
Maximum length for cache filename in parameter file.
friend std::ostream & operator<<(std::ostream &out, const Pars &pars)
std::string to_string() const
Dynamic array container with automatic resizing.