Stroika Library 3.0d23x
 
Loading...
Searching...
No Matches
ToSeekableInputStream.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
7
8namespace Stroika::Foundation::Streams::ToSeekableInputStream {
9
10 /*
11 ********************************************************************************
12 ****************** Streams::ToSeekableInputStream::New *************************
13 ********************************************************************************
14 */
15 template <typename ELEMENT_TYPE>
16 auto New (const Ptr<ELEMENT_TYPE>& in) -> Ptr<ELEMENT_TYPE>
17 {
19 struct seekableWrapper final : InputStreamDelegationHelper<ELEMENT_TYPE> {
20
22 seekableWrapper (const Ptr<ELEMENT_TYPE>& in)
23 : inherited{in}
24 , fOffset_{in.GetOffset ()}
25 , fCacheBaseOffset_{fOffset_}
26 {
27 Assert (not this->fRealIn.IsSeekable ()); // just to document that's why we're here!
28 }
29 virtual bool IsSeekable () const override
30 {
31 return true;
32 }
33 virtual optional<size_t> AvailableToRead () override
34 {
35 SeekOffsetType cacheEnd = fCacheBaseOffset_ + fCachedData_.size ();
36 if (fCacheBaseOffset_ <= fOffset_ and fOffset_ < cacheEnd) [[unlikely]] {
37 Ensure (cacheEnd - fOffset_ > 0);
38 return static_cast<size_t> (cacheEnd - fOffset_);
39 }
40 return this->fRealIn.AvailableToRead ();
41 }
42 virtual optional<SeekOffsetType> RemainingLength () override
43 {
44 auto baseRemaining = this->fRealIn.RemainingLength ();
45 if (baseRemaining) {
46 SeekOffsetType cacheEnd = fCacheBaseOffset_ + fCachedData_.size ();
47 Assert (fOffset_ <= cacheEnd);
48 baseRemaining = *baseRemaining + static_cast<size_t> (cacheEnd - fOffset_); // if we have some cached data past current seek offset, add it too
49 }
50 return baseRemaining;
51 }
52 virtual optional<span<ELEMENT_TYPE>> Read (span<ELEMENT_TYPE> intoBuffer, [[maybe_unused]] NoDataAvailableHandling blockFlag) override
53 {
54 Require (not intoBuffer.empty ());
55 AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
56 /*
57 * See if the request can be serviced from the cached data. If so, do so.
58 */
59 SeekOffsetType cacheEnd = fCacheBaseOffset_ + fCachedData_.size ();
60 if (fCacheBaseOffset_ <= fOffset_ and fOffset_ < cacheEnd) [[unlikely]] {
61 size_t copyCnt = max<size_t> (static_cast<size_t> (cacheEnd - fOffset_), intoBuffer.size ());
62 auto r = Memory::CopyBytes (span{fCachedData_}.subspan (static_cast<size_t> (fOffset_ - fCacheBaseOffset_), copyCnt), intoBuffer);
63 fOffset_ += copyCnt;
64 return r;
65 }
66 /*
67 * If it cannot, accumulate any read data into the cache so it can be re-read.
68 */
69 Assert (fOffset_ == inherited::fRealIn.GetOffset ()); // could be bug with this code or somebody else playing fast and loose, but use assert
70 auto r = this->fRealIn.ReadOrThrow (intoBuffer, blockFlag);
71 // cache it, and update our data structures; note easy, cuz fRealIn must be at matching seek offset
72 fCachedData_.push_back (r);
73 fOffset_ += r.size ();
74 return r;
75 }
76 virtual SeekOffsetType GetReadOffset () const override
77 {
78 AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
79 return fOffset_;
80 }
81 virtual SeekOffsetType SeekRead (Whence whence, SignedSeekOffsetType offset) override
82 {
83 static const auto kException_ = range_error{"seek"};
84 AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
85 switch (whence) {
86 case Whence::eFromStart: {
87 if (offset < 0) [[unlikely]] {
88 Execution::Throw (kException_);
89 }
90 SeekOffsetType newOffset = static_cast<SeekOffsetType> (offset);
91 Require (newOffset >= fCacheBaseOffset_); // as documented in New() code that creates this, we cannot seek back past where we started from
92 SeekOffsetType cacheEnd = fCacheBaseOffset_ + fCachedData_.size ();
93 while (newOffset > cacheEnd) {
94 // we must read and buffer/accumulate in the cache; note - because of how this code works, the
95 // fRealIn is always seeked to the end cached data.
96 byte someBuf[1024];
97 auto r = this->fRealIn.ReadBlocking (span{someBuf});
98 fCachedData_.push_back (r); // nb: an exception in this copy would cause fRealIn offset to be out of sync, but not sure what todo about it
99 cacheEnd = fCacheBaseOffset_ + fCachedData_.size ();
100 }
101 Assert (newOffset <= cacheEnd);
102 fOffset_ = newOffset;
103 return newOffset;
104 } break;
105 case eFromCurrent: {
106 return this->SeekRead (eFromStart, fOffset_ + offset);
107 } break;
108 case eFromEnd: {
109 if (auto remainingLength = this->RemainingLength ()) {
110 return this->SeekRead (eFromStart, fOffset_ + *remainingLength + offset);
111 }
112 else {
113 // implies seeking (fRealIn) to the end, and so reading everything, and then performing the desired seek
114 while (true) {
115 byte someBuf[8 * 1024];
116 auto r = this->fRealIn.ReadBlocking (span{someBuf});
117 fCachedData_.push_back (r); // nb: an exception in this copy would cause fRealIn offset to be out of sync, but not sure what todo about it
118 if (r.empty ()) {
119 break;
120 }
121 }
122 SeekOffsetType realEnd = fCacheBaseOffset_ + fCachedData_.size ();
123 Assert (realEnd == this->fRealIn.GetOffset ());
124 return this->SeekRead (eFromStart, realEnd + offset);
125 }
126 } break;
127 default:
129 return 0;
130 }
131 }
132
133 private:
135 SeekOffsetType fOffset_{0}; // this rep's seek offset (as oppsed to that in fRealIn)
136 SeekOffsetType fCacheBaseOffset_{0};
137 [[no_unique_address]] Debug::AssertExternallySynchronizedMutex fThisAssertExternallySynchronized_;
138 };
139 if (in.IsSeekable ()) {
140 return in;
141 }
142 else {
143 return Ptr<ELEMENT_TYPE>{Memory::MakeSharedPtr<seekableWrapper> (in)};
144 }
145 return in;
146 }
147
148}
#define RequireNotReached()
Definition Assertions.h:385
NoDataAvailableHandling
If eDontBlock passed to most Stream APIs, then when the code would do a blocking read,...
Definition Stream.h:90
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
InputStream<>::Ptr is Smart pointer (with abstract Rep) class defining the interface to reading from ...
nonvirtual SeekOffsetType GetOffset() const