Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
MemoryStream.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Foundation/Containers/Support/ReserveTweaks.h"
7
8namespace Stroika::Foundation::Streams::MemoryStream {
9
10 namespace Private_ {
11 template <typename ELEMENT_TYPE>
12 class Rep_ : public InputOutputStream::IRep<ELEMENT_TYPE> {
13 public:
14 using ElementType = ELEMENT_TYPE;
15
16 private:
17 bool fOpenRead_{true};
18 bool fOpenWrite_{true};
19
20 public:
21 Rep_ ()
22 : fReadCursor_{fData_.begin ()}
23 , fWriteCursor_{fData_.begin ()}
24 {
25 }
26 Rep_ (const Rep_&) = delete;
27 nonvirtual Rep_& operator= (const Rep_&) = delete;
28
29 virtual bool IsSeekable () const override
30 {
31 return true;
32 }
33 virtual void CloseWrite () override
34 {
35 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
36 fOpenWrite_ = false;
37 Ensure (not IsOpenWrite ());
38 }
39 virtual bool IsOpenWrite () const override
40 {
41 return fOpenWrite_;
42 }
43 virtual void CloseRead () override
44 {
45 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
46 fOpenRead_ = false;
47 Ensure (not IsOpenRead ());
48 }
49 virtual bool IsOpenRead () const override
50 {
51 return fOpenRead_;
52 }
53 virtual optional<size_t> AvailableToRead () override
54 {
55 Ensure (fData_.end () >= fReadCursor_);
56 return static_cast<size_t> (fData_.end () - fReadCursor_); // no uncertainty about data available in MemoryStream
57 }
58 virtual optional<SeekOffsetType> RemainingLength () override
59 {
60 Ensure (fData_.end () >= fReadCursor_);
61 return static_cast<size_t> (fData_.end () - fReadCursor_);
62 }
63 virtual optional<span<ELEMENT_TYPE>> Read (span<ELEMENT_TYPE> intoBuffer, [[maybe_unused]] NoDataAvailableHandling blockFlag) override
64 {
65 Require (IsOpenRead ());
66 Require (not intoBuffer.empty ());
67 size_t nRequested = intoBuffer.size ();
68 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
69 Assert ((fData_.begin () <= fReadCursor_) and (fReadCursor_ <= fData_.end ()));
70 size_t nAvail = fData_.end () - fReadCursor_;
71 size_t nCopied = min (nAvail, nRequested);
72 {
73 copy (fReadCursor_, fReadCursor_ + nCopied, intoBuffer.data ());
74 fReadCursor_ = fReadCursor_ + nCopied;
75 }
76 return intoBuffer.subspan (0, nCopied); // this can be empty iff EOF
77 }
78 virtual void Write (span<const ELEMENT_TYPE> elts) override
79 {
80 Require (not elts.empty ());
81 Require (IsOpenWrite ());
82 // @todo - rewrite so does in one copy - no idea why this code does multiple copies! IF it makes sense DOCUMENT why...--LGP 2023-12-18
83 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
84 size_t roomLeft = fData_.end () - fWriteCursor_;
85 size_t roomRequired = elts.size ();
86 if (roomLeft < roomRequired) {
87 size_t curReadOffset = fReadCursor_ - fData_.begin ();
88 size_t curWriteOffset = fWriteCursor_ - fData_.begin ();
89 const size_t kChunkSize_ = 128; // WAG: @todo tune number...
90 Containers::Support::ReserveTweaks::Reserve4AddN (fData_, roomRequired - roomLeft, kChunkSize_);
91 fData_.resize (curWriteOffset + roomRequired); // fixup cursors after any possible realloc of fData_
92 fReadCursor_ = fData_.begin () + curReadOffset;
93 fWriteCursor_ = fData_.begin () + curWriteOffset;
94 Assert (fWriteCursor_ < fData_.end ());
95 }
96 copy (elts.data (), elts.data () + roomRequired, fWriteCursor_);
97 fWriteCursor_ += roomRequired;
98 Assert (fReadCursor_ < fData_.end ()); // < because we wrote at least one byte and that didn't move read cursor
99 Assert (fWriteCursor_ <= fData_.end ());
100 }
101 virtual void Flush () override
102 {
103 Require (IsOpenWrite ());
104 // nothing todo - write 'writes thru'
105 }
106 virtual SeekOffsetType GetReadOffset () const override
107 {
108 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
109 Require (IsOpenRead ());
110 return fReadCursor_ - fData_.begin ();
111 }
112 virtual SeekOffsetType SeekRead (Whence whence, SignedSeekOffsetType offset) override
113 {
114 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
115 Require (IsOpenRead ());
116 switch (whence) {
117 case eFromStart: {
118 if (offset < 0) [[unlikely]] {
119 Execution::Throw (kSeekException_);
120 }
121 SeekOffsetType uOffset = static_cast<SeekOffsetType> (offset);
122 if (uOffset > fData_.size ()) [[unlikely]] {
123 Execution::Throw (EOFException::kThe);
124 }
125 fReadCursor_ = fData_.begin () + static_cast<size_t> (uOffset);
126 } break;
127 case eFromCurrent: {
128 Streams::SeekOffsetType curOffset = fReadCursor_ - fData_.begin ();
129 Streams::SignedSeekOffsetType newOffset = curOffset + offset;
130 if (newOffset < 0) [[unlikely]] {
131 Execution::Throw (kSeekException_);
132 }
133 SeekOffsetType uNewOffset = static_cast<SeekOffsetType> (newOffset);
134 if (uNewOffset > fData_.size ()) [[unlikely]] {
135 Execution::Throw (EOFException::kThe);
136 }
137 fReadCursor_ = fData_.begin () + static_cast<size_t> (uNewOffset);
138 } break;
139 case eFromEnd: {
140 Streams::SignedSeekOffsetType newOffset = fData_.size () + offset;
141 if (newOffset < 0) [[unlikely]] {
142 Execution::Throw (kSeekException_);
143 }
144 SeekOffsetType uNewOffset = static_cast<SeekOffsetType> (newOffset);
145 if (uNewOffset > fData_.size ()) [[unlikely]] {
146 Execution::Throw (EOFException::kThe);
147 }
148 fReadCursor_ = fData_.begin () + static_cast<size_t> (uNewOffset);
149 } break;
150 }
151 Ensure ((fData_.begin () <= fReadCursor_) and (fReadCursor_ <= fData_.end ()));
152 return fReadCursor_ - fData_.begin ();
153 }
154 virtual SeekOffsetType GetWriteOffset () const override
155 {
156 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
157 Require (IsOpenWrite ());
158 return fWriteCursor_ - fData_.begin ();
159 }
160 virtual SeekOffsetType SeekWrite (Whence whence, SignedSeekOffsetType offset) override
161 {
162 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
163 Require (IsOpenWrite ());
164 switch (whence) {
165 case eFromStart: {
166 if (offset < 0) [[unlikely]] {
167 Execution::Throw (kSeekException_);
168 }
169 if (static_cast<SeekOffsetType> (offset) > fData_.size ()) [[unlikely]] {
170 Execution::Throw (EOFException::kThe);
171 }
172 fWriteCursor_ = fData_.begin () + static_cast<size_t> (offset);
173 } break;
174 case eFromCurrent: {
175 Streams::SeekOffsetType curOffset = fWriteCursor_ - fData_.begin ();
176 Streams::SignedSeekOffsetType newOffset = curOffset + offset;
177 if (newOffset < 0) [[unlikely]] {
178 Execution::Throw (kSeekException_);
179 }
180 if (static_cast<size_t> (newOffset) > fData_.size ()) [[unlikely]] {
181 Execution::Throw (EOFException::kThe);
182 }
183 fWriteCursor_ = fData_.begin () + static_cast<size_t> (newOffset);
184 } break;
185 case eFromEnd: {
186 Streams::SignedSeekOffsetType newOffset = fData_.size () + offset;
187 if (newOffset < 0) [[unlikely]] {
188 Execution::Throw (kSeekException_);
189 }
190 if (static_cast<size_t> (newOffset) > fData_.size ()) [[unlikely]] {
191 Execution::Throw (EOFException::kThe);
192 }
193 fWriteCursor_ = fData_.begin () + static_cast<size_t> (newOffset);
194 } break;
195 }
196 Ensure ((fData_.begin () <= fWriteCursor_) and (fWriteCursor_ <= fData_.end ()));
197 return fWriteCursor_ - fData_.begin ();
198 }
199 vector<ElementType> AsVector () const
200 {
201 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
202 return fData_;
203 }
204 string AsString () const
205 {
206 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
207 return string{reinterpret_cast<const char*> (Containers::Start (fData_)), reinterpret_cast<const char*> (Containers::End (fData_))};
208 }
209
210 private:
211 // @todo - COULD redo using
212 // constexpr size_t USE_BUFFER_BYTES = 1024 - sizeof(recursive_mutex) - sizeof(byte*) - sizeof (BinaryInputStream::_IRep) - sizeof (Seekable::_IRep);
213 // Memory::InlineBuffer<byte,USE_BUFFER_BYTES> fData_;
214 // Or Stroika chunked array code
215
216 private:
217 static inline const auto kSeekException_ = range_error{"seek"};
218 vector<ElementType> fData_; // subtle, but important data declared before cursors for initialization CTOR sake
219 typename vector<ElementType>::iterator fReadCursor_;
220 typename vector<ElementType>::iterator fWriteCursor_;
221 [[no_unique_address]] Debug::AssertExternallySynchronizedMutex fThisAssertExternallySynchronized_;
222 };
223 }
224 namespace Private_ {
225 String MemStream2StringHelper_ (const span<const byte>& s); // helper to avoid needless #include of ToString code
226 }
227
228 /*
229 ********************************************************************************
230 **************************** MemoryStream<ELEMENT_TYPE> ************************
231 ********************************************************************************
232 */
233 template <typename ELEMENT_TYPE>
234 inline auto New () -> Ptr<ELEMENT_TYPE>
235 {
236 using MemoryStream::Private_::Rep_;
237 return make_shared<Rep_<ELEMENT_TYPE>> ();
238 }
239 template <typename ELEMENT_TYPE, size_t EXTENT>
240 inline auto New (span<const ELEMENT_TYPE, EXTENT> copyFrom) -> Ptr<ELEMENT_TYPE>
241 {
242 using MemoryStream::Private_::Rep_;
243 Ptr r = make_shared<Rep_<ELEMENT_TYPE>> ();
244 r.Write (copyFrom);
245 return r;
246 }
247 template <typename ELEMENT_TYPE>
248 inline auto New (const Memory::BLOB& copyFrom) -> Ptr<ELEMENT_TYPE>
249 requires (same_as<ELEMENT_TYPE, byte>)
250 {
251 using MemoryStream::Private_::Rep_;
252 Ptr r = make_shared<Rep_<ELEMENT_TYPE>> ();
253 r.Write (copyFrom);
254 return r;
255 }
256
257 /*
258 ********************************************************************************
259 *********************** MemoryStream::Ptr<ELEMENT_TYPE> ************************
260 ********************************************************************************
261 */
262 template <typename ELEMENT_TYPE>
263 inline Ptr<ELEMENT_TYPE>::Ptr (nullptr_t)
264 : inherited{nullptr}
265 {
266 }
267 template <typename ELEMENT_TYPE>
268 inline Ptr<ELEMENT_TYPE>::Ptr (const shared_ptr<Private_::Rep_<ELEMENT_TYPE>>& from)
269 : inherited{from}
270 {
271 }
272 template <typename ELEMENT_TYPE>
273 inline auto Ptr<ELEMENT_TYPE>::GetRepConstRef_ () const -> const Private_::Rep_<ELEMENT_TYPE>&
274 {
275 using Rep_ = typename MemoryStream::Private_::Rep_<ELEMENT_TYPE>;
276 return *Debug::UncheckedDynamicCast<const Rep_*> (&inherited::GetRepConstRef ());
277 }
278 template <typename ELEMENT_TYPE>
279 inline auto Ptr<ELEMENT_TYPE>::GetRepRWRef_ () const -> Private_::Rep_<ELEMENT_TYPE>&
280 {
281 using Rep_ = typename MemoryStream::Private_::Rep_<ELEMENT_TYPE>;
282 return *Debug::UncheckedDynamicCast<Rep_*> (&inherited::GetRepRWRef ());
283 }
284 template <typename ELEMENT_TYPE>
285 template <typename T>
286 inline T Ptr<ELEMENT_TYPE>::As () const
287 requires (same_as<T, vector<ELEMENT_TYPE>> or (same_as<ELEMENT_TYPE, byte> and (same_as<T, BLOB> or same_as<T, string>)) or
288 (same_as<ELEMENT_TYPE, Character> and (same_as<T, String>)))
289 {
290 if constexpr (same_as<T, vector<ELEMENT_TYPE>>) {
291 return GetRepConstRef_ ().AsVector ();
292 }
293 else if constexpr (same_as<T, Memory::BLOB>) {
294 return GetRepConstRef_ ().AsVector ();
295 }
296 else if constexpr (same_as<T, string>) {
297 return GetRepConstRef_ ().AsString ();
298 }
299 else if constexpr (same_as<T, String>) {
300 auto tmp = GetRepConstRef_ ().AsVector ();
301 return String{span{tmp}};
302 }
303 }
304 template <typename ELEMENT_TYPE>
306 {
307 return Private_::MemStream2StringHelper_ (GetRepConstRef_ ().AsVector ());
308 }
309
310 template <typename ELEMENT_TYPE>
311 [[deprecated ("Since Stroika v3.0d5 use span interface")]] static Ptr<ELEMENT_TYPE> New (const ELEMENT_TYPE* start, const ELEMENT_TYPE* end)
312 {
313 return make_shared<Private_::Rep_<ELEMENT_TYPE>> (start, end);
314 }
315
316}
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
shared_lock< const AssertExternallySynchronizedMutex > ReadContext
Instantiate AssertExternallySynchronizedMutex::ReadContext to designate an area of code where protect...
unique_lock< AssertExternallySynchronizedMutex > WriteContext
Instantiate AssertExternallySynchronizedMutex::WriteContext to designate an area of code where protec...
CONTAINER::value_type * End(CONTAINER &c)
For a contiguous container (such as a vector or basic_string) - find the pointer to the end of the co...
CONTAINER::value_type * Start(CONTAINER &c)
For a contiguous container (such as a vector or basic_string) - find the pointer to the start of the ...
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43