Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
BLOB.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
11#include "Stroika/Foundation/Execution/Exceptions.h"
12#include "Stroika/Foundation/Execution/Throw.h"
14
15#include "BLOB.h"
16
17using std::byte;
18
19using namespace Stroika::Foundation;
21using namespace Stroika::Foundation::Debug;
22using namespace Stroika::Foundation::Memory;
23using namespace Stroika::Foundation::Streams;
24
26using Memory::BLOB;
27
28/*
29 ********************************************************************************
30 ************************* Memory::BLOB::BasicRep_ ******************************
31 ********************************************************************************
32 */
33namespace {
34 size_t len_ (const initializer_list<pair<const byte*, const byte*>>& startEndPairs)
35 {
36 size_t sz = 0;
37 for (auto i : startEndPairs) {
38 sz += (i.second - i.first);
39 }
40 return sz;
41 }
42 size_t len_ (const initializer_list<BLOB>& list2Concatenate)
43 {
44 size_t sz = 0;
45 for (const auto& i : list2Concatenate) {
46 sz += i.GetSize ();
47 }
48 return sz;
49 }
50}
51
52BLOB::BasicRep_::BasicRep_ (const initializer_list<pair<const byte*, const byte*>>& startEndPairs)
53 : fData{len_ (startEndPairs)}
54{
55 byte* pb = fData.begin ();
56 for (auto i : startEndPairs) {
57 auto itemSize{i.second - i.first};
58 if (itemSize != 0) {
59 (void)::memcpy (pb, i.first, itemSize);
60 pb += itemSize;
61 }
62 }
63 Ensure (pb == fData.end ());
64}
65
66BLOB::BasicRep_::BasicRep_ (const initializer_list<BLOB>& list2Concatenate)
67 : fData{len_ (list2Concatenate)}
68{
69 byte* pb = fData.begin ();
70 for (const auto& i : list2Concatenate) {
71 auto itemSize{i.GetSize ()};
72 if (itemSize != 0) {
73 (void)::memcpy (pb, i.begin (), itemSize);
74 pb += itemSize;
75 }
76 }
77 Ensure (pb == fData.end ());
78}
79
80span<const byte> BLOB::BasicRep_::GetBounds () const
81{
82 Ensure (fData.begin () <= fData.end ());
83#if qCompilerAndStdLib_span_requires_explicit_type_for_BLOBCVT_Buggy
84 return span<const byte>{fData.begin (), fData.end ()};
85#else
86 return span{fData.begin (), fData.end ()};
87#endif
88}
89
90/*
91 ********************************************************************************
92 ************************** Memory::BLOB::ZeroRep_ ******************************
93 ********************************************************************************
94 */
95span<const byte> BLOB::ZeroRep_::GetBounds () const
96{
97 return span<const byte>{};
98}
99
100/*
101 ********************************************************************************
102 ************************* Memory::BLOB::AdoptRep_ ******************************
103 ********************************************************************************
104 */
105BLOB::AdoptRep_::AdoptRep_ (const byte* start, const byte* end)
106 : fStart{start}
107 , fEnd{end}
108{
109 Require (start <= end);
110}
111
112span<const byte> BLOB::AdoptRep_::GetBounds () const
113{
114 Ensure (fStart <= fEnd);
115 return span<const byte>{fStart, fEnd};
116}
117
118/*
119 ********************************************************************************
120 ********************* Memory::BLOB::AdoptAndDeleteRep_ *************************
121 ********************************************************************************
122 */
123BLOB::AdoptAndDeleteRep_::AdoptAndDeleteRep_ (const byte* start, const byte* end)
124 : fStart{start}
125 , fEnd{end}
126{
127 Require (start <= end);
128}
129
130BLOB::AdoptAndDeleteRep_::~AdoptAndDeleteRep_ ()
131{
132 delete[] fStart;
133}
134
135span<const byte> BLOB::AdoptAndDeleteRep_::GetBounds () const
136{
137 Ensure (fStart <= fEnd);
138 return span<const byte>{fStart, fEnd};
139}
140
141/*
142 ********************************************************************************
143 ********************************* Memory::BLOB *********************************
144 ********************************************************************************
145 */
146BLOB BLOB::FromHex (span<const char> s)
147{
149}
150
152{
154}
155
156BLOB BLOB::FromBase64 (span<const char> s)
157{
158 return Cryptography::Encoding::Algorithm::Base64::Decode (s);
159}
160
162{
164 return BLOB::FromBase64 (*ps);
165 }
166 return BLOB::FromBase64 (span<const char>{s.AsASCII ()}); // will throw in this case cuz if not ascii... oops...
167}
168
169namespace {
170 using namespace Streams;
171 struct BLOBBINSTREAM_ : InputStream::Ptr<byte> {
172 BLOBBINSTREAM_ (const BLOB& b)
173 : InputStream::Ptr<byte>{Memory::MakeSharedPtr<REP> (b)}
174 {
175 }
176 struct REP : InputStream::IRep<byte>, public Memory::UseBlockAllocationIfAppropriate<REP> {
177 bool fIsOpenForRead_{true};
179 BLOB fSavedBLOB_; // save ref to BLOB in case it goes out of scope before stream
180 REP (const BLOB& b)
181 : fSavedBLOB_{b}
182 , fCur{fSavedBLOB_.begin ()}
183 , fStart{fSavedBLOB_.begin ()}
184 , fEnd{fSavedBLOB_.end ()}
185 {
186 }
187 virtual bool IsSeekable () const override
188 {
189 return true;
190 }
191 virtual void CloseRead () override
192 {
193 fIsOpenForRead_ = false;
194 Require (not IsOpenRead ());
195 }
196 virtual bool IsOpenRead () const override
197 {
198 return fIsOpenForRead_;
199 }
200 virtual optional<size_t> AvailableToRead () override
201 {
202 Require (IsOpenRead ());
203 AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized_};
204 return fEnd - fCur;
205 }
206 virtual optional<SeekOffsetType> RemainingLength () override
207 {
208 AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized_};
209 Require (IsOpenRead ());
210 return fEnd - fCur;
211 }
212 virtual optional<span<byte>> Read (span<byte> intoBuffer, [[maybe_unused]] NoDataAvailableHandling blockFlag) override
213 {
214 Require (not intoBuffer.empty ());
215 AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized_};
216 size_t bytesToRead = intoBuffer.size ();
217 size_t bytesLeft = fEnd - fCur;
218 bytesToRead = min (bytesLeft, bytesToRead);
219 CopyBytes (span{fCur, bytesToRead}, intoBuffer);
220 fCur += bytesToRead;
221 return intoBuffer.subspan (0, bytesToRead);
222 }
223 virtual SeekOffsetType GetReadOffset () const override
224 {
225 Require (IsOpenRead ());
226 AssertExternallySynchronizedChecker::ReadContext declareContext{fThisAssertExternallySynchronized_};
227 return fCur - fStart;
228 }
229 virtual SeekOffsetType SeekRead (Whence whence, SignedSeekOffsetType offset) override
230 {
231 static const auto kException_ = range_error{"seek"};
232 AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized_};
233 Require (IsOpenRead ());
234 switch (whence) {
235 case eFromStart: {
236 if (offset < 0) [[unlikely]] {
237 Execution::Throw (kException_);
238 }
239 if (offset > (fEnd - fStart)) [[unlikely]] {
240 Execution::Throw (kException_);
241 }
242 fCur = fStart + offset;
243 } break;
244 case eFromCurrent: {
245 Streams::SeekOffsetType curOffset = fCur - fStart;
246 Streams::SignedSeekOffsetType newOffset = curOffset + offset;
247 if (newOffset < 0) [[unlikely]] {
248 Execution::Throw (kException_);
249 }
250 if (newOffset > (fEnd - fStart)) [[unlikely]] {
251 Execution::Throw (kException_);
252 }
253 fCur = fStart + newOffset;
254 } break;
255 case eFromEnd: {
256 Streams::SignedSeekOffsetType newOffset = (fEnd - fStart) + offset;
257 if (newOffset < 0) [[unlikely]] {
258 Execution::Throw (kException_);
259 }
260 if (newOffset > (fEnd - fStart)) [[unlikely]] {
261 Execution::Throw (kException_);
262 }
263 fCur = fStart + newOffset;
264 } break;
265 }
266 Ensure ((fStart <= fCur) and (fCur <= fEnd));
267 return GetReadOffset ();
268 }
269 const byte* fCur;
270 const byte* fStart;
271 const byte* fEnd;
272 };
273 };
274}
275
276template <>
278{
279 AssertExternallySynchronizedChecker::ReadContext declareContext{fThisAssertExternallySynchronized_};
280 return BLOBBINSTREAM_{*this};
281}
282
283template <>
285{
286 AssertExternallySynchronizedChecker::ReadContext declareContext{fThisAssertExternallySynchronized_};
287 span<const byte> bytes = As<span<const byte>> ();
288 return String{Cryptography::Encoding::Algorithm::Hex::Encode (bytes.subspan (0, min (bytes.size (), maxBytesToShow)))};
289}
290
291template <>
293{
294 return AsBase64 ({});
295}
296template <>
297Characters::String Stroika::Foundation::Memory::BLOB::AsBase64 (const Cryptography::Encoding::Algorithm::Base64::Options& o) const
298{
300}
301
302BLOB BLOB::Repeat (unsigned int count) const
303{
304 AssertExternallySynchronizedChecker::ReadContext declareContext{fThisAssertExternallySynchronized_};
305 if (count == 0) {
306 return BLOB{};
307 }
308 else if (count <= 2) {
309 BLOB tmp = *this;
310 for (unsigned int i = 1; i < count; ++i) {
311 tmp = tmp + *this;
312 }
313 return tmp;
314 }
315 else {
316 // speed tweak
317 span<const byte> b2c = As<span<const byte>> ();
319 target.reserve (count * b2c.size ());
320 for (unsigned int i = 0; i < count; ++i) {
321 target.push_back (b2c);
322 }
323 return BLOB{span{target}};
324 }
325}
326
327BLOB BLOB::Slice (size_t startAt, size_t endAt) const
328{
329 Require (startAt <= endAt);
330 Require (endAt < size ());
331 AssertExternallySynchronizedChecker::ReadContext declareContext{fThisAssertExternallySynchronized_};
332 return BLOB{begin () + startAt, begin () + endAt};
333}
334
335String BLOB::ToString (size_t maxBytesToShow) const
336{
337 AssertExternallySynchronizedChecker::ReadContext declareContext{fThisAssertExternallySynchronized_};
338 bool allBytesAscii = [this] () { return Character::IsASCII (Memory::SpanBytesCast<span<const char>> (this->As<span<const byte>> ())); }();
339 auto quoteAscii4Display = [] (const String& s) {
340 return s.ReplaceAll ("\n"sv, "\\n"sv).ReplaceAll ("\r"sv, "\\r"sv); // todo more such
341 };
342 if (size () > maxBytesToShow) {
343 if (allBytesAscii) {
344 return "[{} bytes: '{}' ...]"_f(size (), quoteAscii4Display (String{this->As<string> ()}));
345 }
346 else {
347 String hexStr = AsHex (maxBytesToShow + 1); // so we can replace/ellipsis with LimitLength ()
349 return "[{} bytes: {}]"_f(size (), hexStr.LimitLength (maxStrLen));
350 }
351 }
352 else {
353 if (allBytesAscii) {
354 return "[{} bytes: '{}']"_f(size (), quoteAscii4Display (String{this->As<string> ()}));
355 }
356 else {
357 return "[{} bytes: {}]"_f(size (), AsHex ());
358 }
359 }
360}
conditional_t< qStroika_Foundation_Memory_PreferBlockAllocation and andTrueCheck, BlockAllocationUseHelper< T >, Common::Empty > UseBlockAllocationIfAppropriate
Use this to enable block allocation for a particular class. Beware of subclassing.
#define Stroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE
[[msvc::no_unique_address]] isn't always broken in MSVC. Annotate with this on things where its not b...
Definition StdCompat.h:481
NoDataAvailableHandling
If eDontBlock passed to most Stream APIs, then when the code would do a blocking read,...
Definition Stream.h:90
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
unique_lock< AssertExternallySynchronizedChecker > WriteContext
Instantiate AssertExternallySynchronizedChecker::WriteContext to designate an area of code where prot...
shared_lock< const AssertExternallySynchronizedChecker > ReadContext
Instantiate AssertExternallySynchronizedChecker::ReadContext to designate an area of code where prote...
nonvirtual STRING_TYPE AsBase64() const
nonvirtual Characters::String ToString(size_t maxBytesToShow=80) const
Definition BLOB.cpp:335
nonvirtual BLOB Repeat(unsigned int count) const
Definition BLOB.cpp:302
static BLOB FromHex(const char *b)
Convert string of hex bytes to BLOB.
Definition BLOB.inl:127
nonvirtual BLOB Slice(size_t startAt, size_t endAt) const
Definition BLOB.cpp:327
nonvirtual const byte * begin() const
Definition BLOB.inl:258
nonvirtual STRING_TYPE AsHex(size_t maxBytesToShow=numeric_limits< size_t >::max()) const
nonvirtual size_t GetSize() const
Definition BLOB.inl:269
static BLOB FromBase64(const char *b)
Convert string of base64 bytes to BLOB.
Definition BLOB.inl:140
nonvirtual size_t size() const
Definition BLOB.inl:286
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
nonvirtual void reserve(size_t newCapacity, bool atLeast=true)
InputStream<>::Ptr is Smart pointer (with abstract Rep) class defining the interface to reading from ...
A Streams::Ptr<ELEMENT_TYPE> is a smart-pointer to a stream of elements of type T.
Definition Stream.h:170
char ASCII
Stroika's string/character classes treat 'char' as being an ASCII character.
Definition Character.h:59
string Encode(const Streams::InputStream::Ptr< byte > &from, const Options &o={})
Definition Base64.cpp:303
string Encode(span< const byte > from)
The bytes as hex: two digits a byte, lower case - {0x29, 0x14} as "2914".
Definition Hex.cpp:24
Memory::BLOB Decode(span< const char > s)
Hex read back into bytes: digits of either case; spaces before a byte's two digits skipped (so "29 14...
Definition Hex.cpp:41