Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
BLOB.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
10#include "Stroika/Foundation/Execution/Exceptions.h"
11#include "Stroika/Foundation/Execution/Throw.h"
13
14#include "BLOB.h"
15
16using std::byte;
17
18using namespace Stroika::Foundation;
20using namespace Stroika::Foundation::Debug;
21using namespace Stroika::Foundation::Memory;
22using namespace Stroika::Foundation::Streams;
23
25using Memory::BLOB;
26
27#if qCompilerAndStdLib_specializeDeclarationRequiredSometimesToGenCode_Buggy
28template <>
30#endif
31
32/*
33 ********************************************************************************
34 ************************* Memory::BLOB::BasicRep_ ******************************
35 ********************************************************************************
36 */
37namespace {
38 size_t len_ (const initializer_list<pair<const byte*, const byte*>>& startEndPairs)
39 {
40 size_t sz = 0;
41 for (auto i : startEndPairs) {
42 sz += (i.second - i.first);
43 }
44 return sz;
45 }
46 size_t len_ (const initializer_list<BLOB>& list2Concatenate)
47 {
48 size_t sz = 0;
49 for (const auto& i : list2Concatenate) {
50 sz += i.GetSize ();
51 }
52 return sz;
53 }
54}
55
56BLOB::BasicRep_::BasicRep_ (const initializer_list<pair<const byte*, const byte*>>& startEndPairs)
57 : fData{len_ (startEndPairs)}
58{
59 byte* pb = fData.begin ();
60 for (auto i : startEndPairs) {
61 auto itemSize{i.second - i.first};
62 if (itemSize != 0) {
63 (void)::memcpy (pb, i.first, itemSize);
64 pb += itemSize;
65 }
66 }
67 Ensure (pb == fData.end ());
68}
69
70BLOB::BasicRep_::BasicRep_ (const initializer_list<BLOB>& list2Concatenate)
71 : fData{len_ (list2Concatenate)}
72{
73 byte* pb = fData.begin ();
74 for (const auto& i : list2Concatenate) {
75 auto itemSize{i.GetSize ()};
76 if (itemSize != 0) {
77 (void)::memcpy (pb, i.begin (), itemSize);
78 pb += itemSize;
79 }
80 }
81 Ensure (pb == fData.end ());
82}
83
84span<const byte> BLOB::BasicRep_::GetBounds () const
85{
86 Ensure (fData.begin () <= fData.end ());
87#if qCompilerAndStdLib_span_requires_explicit_type_for_BLOBCVT_Buggy
88 return span<const byte>{fData.begin (), fData.end ()};
89#else
90 return span{fData.begin (), fData.end ()};
91#endif
92}
93
94/*
95 ********************************************************************************
96 ************************** Memory::BLOB::ZeroRep_ ******************************
97 ********************************************************************************
98 */
99span<const byte> BLOB::ZeroRep_::GetBounds () const
100{
101 return span<const byte>{};
102}
103
104/*
105 ********************************************************************************
106 ************************* Memory::BLOB::AdoptRep_ ******************************
107 ********************************************************************************
108 */
109BLOB::AdoptRep_::AdoptRep_ (const byte* start, const byte* end)
110 : fStart{start}
111 , fEnd{end}
112{
113 Require (start <= end);
114}
115
116span<const byte> BLOB::AdoptRep_::GetBounds () const
117{
118 Ensure (fStart <= fEnd);
119 return span<const byte>{fStart, fEnd};
120}
121
122/*
123 ********************************************************************************
124 ********************* Memory::BLOB::AdoptAndDeleteRep_ *************************
125 ********************************************************************************
126 */
127BLOB::AdoptAndDeleteRep_::AdoptAndDeleteRep_ (const byte* start, const byte* end)
128 : fStart{start}
129 , fEnd{end}
130{
131 Require (start <= end);
132}
133
134BLOB::AdoptAndDeleteRep_::~AdoptAndDeleteRep_ ()
135{
136 delete[] fStart;
137}
138
139span<const byte> BLOB::AdoptAndDeleteRep_::GetBounds () const
140{
141 Ensure (fStart <= fEnd);
142 return span<const byte>{fStart, fEnd};
143}
144
145/*
146 ********************************************************************************
147 ********************************* Memory::BLOB *********************************
148 ********************************************************************************
149 */
150BLOB BLOB::FromHex (span<const char> s)
151{
152 auto HexChar2Num_ = [] (char c) -> byte {
153 if ('0' <= c and c <= '9') [[likely]] {
154 return static_cast<byte> (c - '0');
155 }
156 if ('A' <= c and c <= 'F') [[likely]] {
157 return static_cast<byte> ((c - 'A') + 10);
158 }
159 if ('a' <= c and c <= 'f') [[likely]] {
160 return static_cast<byte> ((c - 'a') + 10);
161 }
162 static const Execution::RuntimeErrorException kException_{"Invalid HEX character in BLOB::Hex"sv};
163 Execution::Throw (kException_);
164 };
166 const char* e = s.data () + s.size ();
167 for (const char* i = s.data (); i < e; ++i) {
168 if (isspace (*i)) [[unlikely]] {
169 continue;
170 }
171 byte b = HexChar2Num_ (*i);
172 ++i;
173 if (i == e) [[unlikely]] {
174 static const Execution::RuntimeErrorException kException_{"Invalid partial HEX character in BLOB::Hex"sv};
175 Execution::Throw (kException_);
176 }
177 b = byte (uint8_t (b << 4) + uint8_t (HexChar2Num_ (*i)));
178 buf.push_back (b);
179 }
180 return BLOB{buf.begin (), buf.end ()};
181}
182
184{
185 if (optional<span<const Characters::ASCII>> ps = s.PeekData<Characters::ASCII> ()) [[likely]] {
186 return BLOB::FromHex (*ps);
187 }
188 return BLOB::FromHex (span<const char>{s.AsASCII ()}); // will throw in this case cuz if not ascii... oops...
189}
190
191BLOB BLOB::FromBase64 (span<const char> s)
192{
193 return Cryptography::Encoding::Algorithm::Base64::Decode (s);
194}
195
197{
198 if (optional<span<const Characters::ASCII>> ps = s.PeekData<Characters::ASCII> ()) [[likely]] {
199 return BLOB::FromBase64 (*ps);
200 }
201 return BLOB::FromBase64 (span<const char>{s.AsASCII ()}); // will throw in this case cuz if not ascii... oops...
202}
203
204namespace {
205 using namespace Streams;
206 struct BLOBBINSTREAM_ : InputStream::Ptr<byte> {
207 BLOBBINSTREAM_ (const BLOB& b)
208 : InputStream::Ptr<byte>{Memory::MakeSharedPtr<REP> (b)}
209 {
210 }
211 struct REP : InputStream::IRep<byte>, public Memory::UseBlockAllocationIfAppropriate<REP> {
212 bool fIsOpenForRead_{true};
213 [[no_unique_address]] AssertExternallySynchronizedMutex fThisAssertExternallySynchronized_;
214 BLOB fSavedBLOB_; // save ref to BLOB in case it goes out of scope before stream
215 REP (const BLOB& b)
216 : fSavedBLOB_{b}
217 , fCur{fSavedBLOB_.begin ()}
218 , fStart{fSavedBLOB_.begin ()}
219 , fEnd{fSavedBLOB_.end ()}
220 {
221 }
222 virtual bool IsSeekable () const override
223 {
224 return true;
225 }
226 virtual void CloseRead () override
227 {
228 fIsOpenForRead_ = false;
229 Require (not IsOpenRead ());
230 }
231 virtual bool IsOpenRead () const override
232 {
233 return fIsOpenForRead_;
234 }
235 virtual optional<size_t> AvailableToRead () override
236 {
237 Require (IsOpenRead ());
238 AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
239 return fEnd - fCur;
240 }
241 virtual optional<SeekOffsetType> RemainingLength () override
242 {
243 AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
244 Require (IsOpenRead ());
245 return fEnd - fCur;
246 }
247 virtual optional<span<byte>> Read (span<byte> intoBuffer, [[maybe_unused]] NoDataAvailableHandling blockFlag) override
248 {
249 Require (not intoBuffer.empty ());
250 AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
251 size_t bytesToRead = intoBuffer.size ();
252 size_t bytesLeft = fEnd - fCur;
253 bytesToRead = min (bytesLeft, bytesToRead);
254 CopyBytes (span{fCur, bytesToRead}, intoBuffer);
255 fCur += bytesToRead;
256 return intoBuffer.subspan (0, bytesToRead);
257 }
258 virtual SeekOffsetType GetReadOffset () const override
259 {
260 Require (IsOpenRead ());
261 AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
262 return fCur - fStart;
263 }
264 virtual SeekOffsetType SeekRead (Whence whence, SignedSeekOffsetType offset) override
265 {
266 static const auto kException_ = range_error{"seek"};
267 AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
268 Require (IsOpenRead ());
269 switch (whence) {
270 case eFromStart: {
271 if (offset < 0) [[unlikely]] {
272 Execution::Throw (kException_);
273 }
274 if (offset > (fEnd - fStart)) [[unlikely]] {
275 Execution::Throw (kException_);
276 }
277 fCur = fStart + offset;
278 } break;
279 case eFromCurrent: {
280 Streams::SeekOffsetType curOffset = fCur - fStart;
281 Streams::SignedSeekOffsetType newOffset = curOffset + offset;
282 if (newOffset < 0) [[unlikely]] {
283 Execution::Throw (kException_);
284 }
285 if (newOffset > (fEnd - fStart)) [[unlikely]] {
286 Execution::Throw (kException_);
287 }
288 fCur = fStart + newOffset;
289 } break;
290 case eFromEnd: {
291 Streams::SignedSeekOffsetType newOffset = (fEnd - fStart) + offset;
292 if (newOffset < 0) [[unlikely]] {
293 Execution::Throw (kException_);
294 }
295 if (newOffset > (fEnd - fStart)) [[unlikely]] {
296 Execution::Throw (kException_);
297 }
298 fCur = fStart + newOffset;
299 } break;
300 }
301 Ensure ((fStart <= fCur) and (fCur <= fEnd));
302 return GetReadOffset ();
303 }
304 const byte* fCur;
305 const byte* fStart;
306 const byte* fEnd;
307 };
308 };
309}
310
311template <>
313{
314 AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
315 return BLOBBINSTREAM_{*this};
316}
317
318template <>
320{
321 // @todo Could be more efficient
322 AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
323 StringBuilder sb;
324 size_t cnt{};
325 for (byte b : *this) {
326 if (cnt++ > maxBytesToShow) {
327#if qCompilerAndStdLib_crash_compiling_break_in_forLoop_Buggy
328 return sb.str ();
329#else
330 break;
331#endif
332 }
333 sb << "{:02x}"_f(static_cast<unsigned int> (b));
334 }
335 return sb;
336}
337
338template <>
340{
341 return AsBase64 ({});
342}
343template <>
344Characters::String Stroika::Foundation::Memory::BLOB::AsBase64 (const Cryptography::Encoding::Algorithm::Base64::Options& o) const
345{
346 return Cryptography::Encoding::Algorithm::Base64::Encode (*this, o);
347}
348
349BLOB BLOB::Repeat (unsigned int count) const
350{
351 AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
352 if (count == 0) {
353 return BLOB{};
354 }
355 else if (count <= 2) {
356 BLOB tmp = *this;
357 for (unsigned int i = 1; i < count; ++i) {
358 tmp = tmp + *this;
359 }
360 return tmp;
361 }
362 else {
363 // speed tweak
364 span<const byte> b2c = As<span<const byte>> ();
365 InlineBuffer<byte> target{};
366 target.reserve (count * b2c.size ());
367 for (unsigned int i = 0; i < count; ++i) {
368 target.push_back (b2c);
369 }
370 return BLOB{span{target}};
371 }
372}
373
374BLOB BLOB::Slice (size_t startAt, size_t endAt) const
375{
376 Require (startAt <= endAt);
377 Require (endAt < size ());
378 AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
379 return BLOB{begin () + startAt, begin () + endAt};
380}
381
382String BLOB::ToString (size_t maxBytesToShow) const
383{
384 AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
385 bool allBytesAscii = [this] () { return Character::IsASCII (Memory::SpanBytesCast<span<const char>> (this->As<span<const byte>> ())); }();
386 auto quoteAscii4Display = [] (const String& s) {
387 return s.ReplaceAll ("\n"sv, "\\n"sv).ReplaceAll ("\r"sv, "\\r"sv); // todo more such
388 };
389 if (size () > maxBytesToShow) {
390 if (allBytesAscii) {
391 return "[{} bytes: '{}' ...]"_f(size (), quoteAscii4Display (String{this->As<string> ()}));
392 }
393 else {
394 String hexStr = AsHex (maxBytesToShow + 1); // so we can replace/ellipsis with LimitLength ()
395 size_t maxStrLen = maxBytesToShow < numeric_limits<size_t>::max () / 2 ? maxBytesToShow * 2 : maxBytesToShow;
396 return "[{} bytes: {}]"_f(size (), hexStr.LimitLength (maxStrLen));
397 }
398 }
399 else {
400 if (allBytesAscii) {
401 return "[{} bytes: '{}']"_f(size (), quoteAscii4Display (String{this->As<string> ()}));
402 }
403 else {
404 return "[{} bytes: {}]"_f(size (), AsHex ());
405 }
406 }
407}
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.
NoDataAvailableHandling
If eDontBlock passed to most Stream APIs, then when the code would do a blocking read,...
Definition Stream.h:90
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual String LimitLength(size_t maxLen, StringShorteningPreference keepPref=StringShorteningPreference::ePreferKeepLeft) const
return the first maxLen (or fewer if string shorter) characters of this string (adding ellipsis if tr...
Definition String.inl:741
nonvirtual String ReplaceAll(const RegularExpression &regEx, const String &with) const
Definition String.cpp:1155
static optional< span< const CHAR_TYPE > > PeekData(const PeekSpanData &pds)
return the constant character data inside the string in the form of a span or nullopt if not availabl...
Definition String.inl:906
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
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...
nonvirtual STRING_TYPE AsBase64() const
nonvirtual Characters::String ToString(size_t maxBytesToShow=80) const
Definition BLOB.cpp:382
nonvirtual BLOB Repeat(unsigned int count) const
Definition BLOB.cpp:349
static BLOB FromHex(const char *b)
Convert string of hex bytes to BLOB.
Definition BLOB.inl:122
nonvirtual BLOB Slice(size_t startAt, size_t endAt) const
Definition BLOB.cpp:374
nonvirtual const byte * begin() const
Definition BLOB.inl:253
nonvirtual STRING_TYPE AsHex(size_t maxBytesToShow=numeric_limits< size_t >::max()) const
static BLOB FromBase64(const char *b)
Convert string of base64 bytes to BLOB.
Definition BLOB.inl:135
nonvirtual size_t size() const
Definition BLOB.inl:281
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
nonvirtual pointer data() noexcept
returns a (possibly const) pointer to the start of the live buffer data. This return value can be inv...
nonvirtual void push_back(Common::ArgByValueType< T > e)
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
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43