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