Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
BLOB.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Memory_BLOB_h_
5#define _Stroika_Foundation_Memory_BLOB_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <array>
10#include <compare>
11#include <memory>
12#include <vector>
13
14#include "Stroika/Foundation/Common/Common.h"
15#include "Stroika/Foundation/Common/Concepts.h"
17#include "Stroika/Foundation/Memory/Common.h"
18#include "Stroika/Foundation/Streams/InputStream.h" // maybe bad - leads to circularity problems but hard to pre-declare InputStream
19
20/**
21 * \file
22 *
23 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
24 *
25 */
26
28 class String;
29}
30
32 struct Options;
33}
34
35namespace Stroika::Foundation::Memory {
36
37 /**
38 * A BLOB is a read-only binary region of memory. Once a BLOB is constructed, the data inside cannot
39 * change (except by assignment - being assigned over).
40 *
41 * A BLOB can be thought of as a 'forever' span<const byte>.
42 *
43 * \note Satisfies Concepts:
44 * o totally_ordered<BLOB>
45 * o regular<BLOB>
46 * o Common::explicitly_convertible_to<BLOB, span<const byte>>
47 *
48 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
49 *
50 * \note <a href="Design-Overview.md#Comparisons">Comparisons</a>:
51 * o static_assert (totally_ordered<BLOB>);
52 *
53 * This is like memcmp() - bytewise unsigned comparison
54 *
55 * \note Interactions with Memory::MemoryMappedFileReader
56 * We provide no AUTOMATIC way to combine these, because its not safe (in general, but can be given specific application
57 * knowledge). And it can be quite efficient. So use BLOB::Adopt() on some existing MemoryMappedFileReader - but its up to use/
58 * application to assure the lifetime of the file reader is > any copies of the shared_rep derived from the adopted BLOB.
59 *
60 * \note Performance
61 * o Copying a BLOB is just copying a shared_ptr
62 * o Allocation should be extremely cheap, due to the use of make_shared<> or allocate_shared<> or
63 * Memory::UseBlockAllocationIfAppropriate<>
64 * which should use the block allocation storage mechanism, which is generally a lock free very fast allocator.
65 * And the use of InlineBuffer<64> means that allocation of BLOBs of size <= 64 should requite no calls to the
66 * global ::operator new/malloc/free/delete
67 */
68 class BLOB {
69 protected:
70 struct _IRep;
71
72 public:
73 /**
74 * \par Example Usage
75 * \code
76 * BLOB b1 = BLOB{ 0x29, 0x14, 0x4a };
77 * \endcode
78 *
79 * \see also FromHex, FromRaw (constructor like static functions with special names to make treatment more clear)
80 * \see also Attach, and AttachAndDelete, more rarely useful (and dangerous).
81 */
82 BLOB ();
83 // clang-format off
84 BLOB (BLOB&& src) noexcept = default;
85 BLOB (const BLOB& src) noexcept = default;
86 // clang-format on
87 template <ranges::range CONTAINER_OF_BYTE>
91 BLOB (span<const byte> s);
93 BLOB (const byte* start, const byte* end);
94 BLOB (const uint8_t* start, const uint8_t* end);
99
100 protected:
101 /**
102 * Subclass BLOB, and provider your own 'rep' type, to create more efficient storage.
103 */
104 explicit BLOB (const shared_ptr<_IRep>& rep);
105 explicit BLOB (shared_ptr<_IRep>&& rep);
106
107 public:
108 /**
109 */
110 nonvirtual BLOB& operator= (BLOB&& rhs) noexcept = default;
111 nonvirtual BLOB& operator= (const BLOB& rhs) = default;
112
113 public:
114 /**
115 * \brief Convert string of hex bytes to BLOB.
116 *
117 * Like a constructor, but where you clearly name the intention of how to interpret the
118 * bytes.
119 *
120 * Spaces allowed, but treat as array of (possibly space delimited) hex bytes to BLOB.
121 *
122 * Upper/LowerCase OK, but invalid characters generate throw (Cryptography::Encoding::Algorithm::Hex::Decode).
123 *
124 * \par Example Usage
125 * \code
126 * Assert ((BLOB::FromHex ("29144adb4ece20450956e813652fe8d6") == BLOB { 0x29, 0x14, 0x4a, 0xdb, 0x4e, 0xce, 0x20, 0x45, 0x09, 0x56, 0xe8, 0x13, 0x65, 0x2f, 0xe8, 0xd6 }));
127 * \endcode
128 *
129 * \todo probably allow optionally leading 0x....
130 */
131 static BLOB FromHex (const char* b);
133 static BLOB FromHex (string_view s);
134 static BLOB FromHex (const Characters::String& s);
135
136 public:
137 /**
138 * \brief Convert string of base64 bytes to BLOB.
139 *
140 * Like a constructor, but where you clearly name the intention of how to interpret the bytes.
141 *
142 * Spaces allowed, but treat as array of (possibly space delimited) base64 bytes to BLOB.
143 *
144 * Upper/LowerCase OK, but invalid characters generate throw.
145 *
146 * \par Example Usage
147 * \code
148 * Assert ((BLOB::FromBase64 ("aGVsbG8=") == BLOB { 'h', 'e', 'l', 'l', 'o' }));
149 * \endcode
150 *
151 * \todo probably allow optionally leading 0x....
152 */
153 static BLOB FromBase64 (const char* b);
155 static BLOB FromBase64 (string_view s);
156 static BLOB FromBase64 (const Characters::String& s);
157
158 public:
159 /**
160 * \brief Convert pointed to/referenced data to BLOB (treating the argument as raw bytes).
161 *
162 * Like a constructor, but where you clearly name the intention of how to interpret the
163 * bytes.
164 *
165 * This does little more than a cast (taking into account sizeof T).
166 *
167 * Overloads taking const char*, or const wchar_t* only (char_traits<T>), are assumed to be C-Strings (NUL-terminated).
168 *
169 * \note ALL overloads require T is be 'trivially_copyable' - just like memcpy()
170 */
171 template <Common::trivially_copyable T>
172 static BLOB FromRaw (const T* s, const T* e);
173 template <Common::trivially_copyable T>
174 static BLOB FromRaw (const T* s, size_t sz);
175 template <Common::trivially_copyable T>
176 static BLOB FromRaw (const T* s)
178 template <Common::trivially_copyable T>
179 static BLOB FromRaw (const basic_string<T>& s)
181 template <Common::trivially_copyable T>
182 static BLOB FromRaw (const T& s);
183
184 public:
185 /*
186 * \brief Create a BLOB from the given data - without copying the data (dangerous if not used carefully, but can be used to efficiently reference constant data).
187 *
188 * \note its ILLEGAL and may cause grave disorder, if the caller changes the data passed to Attach() while the derived BLOB (or a copy) exists.
189 *
190 * Typically this is intended to be used to wrap permanent constant data, such as static (text space) read-only data, ROM stuff, etc. It can be used to wrap
191 * data in memory mapped files, but IFF that file mapping will remain permanent (and data immutable). USE WITH CAUTION!
192 *
193 * \see also AttachAndDelete
194 */
195 template <typename BYTEISH, size_t EXTENT = dynamic_extent>
196 static BLOB Attach (span<BYTEISH, EXTENT> s)
198 template <typename BYTEISH, size_t EXTENT>
199 static BLOB Attach (BYTEISH (&data)[EXTENT])
201
202 public:
203 /*
204 * \brief like Attach () - but at last reference to BLOB, will call delete[] data
205 *
206 * RARELY useful, but could be needed if you must manually fill in the data after allocation, before wrapping it in a BLOB.
207 * Note - because of how the data is deleted, you must allocate with new byte[nnn].
208 */
209 static BLOB AttachAndDelete (const byte* s, size_t arrayLen);
210
211 public:
212 /**
213 * \pre i < size ();
214 */
215 nonvirtual byte operator[] (const size_t i) const;
216
217 public:
218 /**
219 * Returns true iff the size of the BLOB is zero.
220 */
221 nonvirtual bool empty () const;
222
223 public:
224 /**
225 * Convert BLOB losslessly into a standard C++ type.
226 * Supported Types for 'T' include:
227 * o span<const byte>
228 * o span<const uint8_t>
229 * o pair<const byte*, const byte*>
230 * o pair<const uint8_t*, const uint8_t*>
231 * o vector<byte>
232 * o vector<uint8_t>
233 * o Streams::InputStream::Ptr<byte>
234 * o string (bytes as characters - note this MAY include NUL-bytes - https://stackoverflow.com/questions/2845769/can-a-stdstring-contain-embedded-nulls)
235 * o any T where is_trivially_copyable
236 *
237 * \note If T is span<> or pair<> (byte or uint8_t) - this returns INTERNAL pointers into the BLOB storage,
238 * so use with care.
239 */
240 template <typename T>
241 nonvirtual T As () const
242#if !qCompilerAndStdLib_template_requires_doesnt_work_with_specialization_Buggy
243 // clang-format off
244 requires (
247 )
248#endif
249 ;
250 // clang-format on
251
252 public:
253 /**
254 * Return a string of hex bytes - two characters per byte, lower case HEX characters (Cryptography::Encoding::Algorithm::Hex::Encode) -
255 * of the first maxBytesToShow bytes.
256 *
257 * \par Example Usage
258 * \code
259 * Assert (BLOB::FromHex ("29144adb4ece20450956e813652fe8d6").AsHex () == "29144adb4ece20450956e813652fe8d6");
260 * \endcode
261 *
262 * \see also FromHex ()
263 */
264 template <typename STRING_TYPE = Characters::String>
265 nonvirtual STRING_TYPE AsHex (size_t maxBytesToShow = numeric_limits<size_t>::max ()) const
266#if !qCompilerAndStdLib_template_requires_doesnt_work_with_specialization_Buggy
268#endif
269 ;
270
271 public:
272 /**
273 * Return a string of base64 encoded bytes.
274 *
275 * \par Example Usage
276 * \code
277 * BLOB{'h', 'e', 'l', 'l', 'o'}.AsBase64 () == "aGVsbG8=")
278 * \endcode
279 *
280 * \see also AsHex (), FromBase64
281 */
282 template <typename STRING_TYPE = Characters::String>
284#if !qCompilerAndStdLib_template_requires_doesnt_work_with_specialization_Buggy
286#endif
287 ;
288 template <typename STRING_TYPE = Characters::String>
289 nonvirtual STRING_TYPE AsBase64 (const Cryptography::Encoding::Algorithm::Base64::Options& o) const
290#if !qCompilerAndStdLib_template_requires_doesnt_work_with_specialization_Buggy
292#endif
293 ;
294
295 public:
296 /**
297 * EXPERIMENMT MAKING THIS NON-EXPLICIT conversion operator. Makes a bunch of other code simpler, and it makes sense.
298 * Trouble is - will it cause ambiguity later, and need to be removed. No obvious way to tell but testing a bit..
299 * --LGP 2023-12-21
300 */
301 /*explicit*/ operator Streams::InputStream::Ptr<byte> () const;
302
303 public:
304 /**
305 * Return a BLOB made by concatenating this BLOB count times.
306 */
307 nonvirtual BLOB Repeat (unsigned int count) const;
308
309 public:
310 /**
311 * Return a BLOB made from the given sub-range of bytes.
312 * \pre startAt <= endAt
313 * \pre endAt < GetSize ()
314 */
315 nonvirtual BLOB Slice (size_t startAt, size_t endAt) const;
316
317 public:
318 /**
319 * Pointers returned by begin(), remain valid for the lifetime of the containing BLOB.
320 * \note - this is legal to call even on an empty BLOB (but may return nullptr)
321 */
322 nonvirtual const byte* begin () const;
323
324 public:
325 /**
326 * Pointers returned by end(), remain valid for the lifetime of the containing BLOB.
327 * \note - this is legal to call even on an empty BLOB (but may return nullptr)
328 */
329 nonvirtual const byte* end () const;
330
331 public:
332 /**
333 * Returns the number of bytes in the BLOB.
334 */
335 nonvirtual size_t GetSize () const;
336
337 public:
338 /**
339 */
340 nonvirtual strong_ordering operator<=> (const BLOB& rhs) const;
341
342 public:
343 /**
344 */
345 nonvirtual bool operator== (const BLOB& rhs) const;
346
347 private:
348 static strong_ordering TWC_ (const BLOB& lhs, const BLOB& rhs); // utility code share between c++17 and c++20 versions
349
350 public:
351 /**
352 * @todo cleanup and switch to spans!!
353 */
354 nonvirtual const byte* data () const;
355
356 public:
357 /**
358 * @aliases GetSize()
359 */
360 nonvirtual size_t size () const;
361
362 public:
363 /**
364 * @aliases GetSize()
365 */
366 nonvirtual size_t length () const;
367
368 public:
369 /**
370 * @see Characters::ToString()
371 * Return a debug-friendly, display version of the current BLOB. This is not guaranteed parsable or usable except for debugging.
372 *
373 * \note this displays the BLOB as hex bytes. To interpret as a string, try
374 * \code
375 * Assert (BinaryToText::Reader::New (blob).ReadAll () == "hello world");
376 * \endcode
377 */
378 nonvirtual Characters::String ToString (size_t maxBytesToShow = 80) const;
379
380 public:
381 /**
382 * Trivial alias BLOB ({*this, rhs});
383 */
384 nonvirtual BLOB operator+ (const BLOB& rhs) const;
385
386 public:
387 [[deprecated ("Since Stroika v3.0d5 use span overload")]] static BLOB FromHex (const char* s, const char* e);
388 template <typename T>
389 [[deprecated ("Since Stroika v3.0d5 - use As/0")]] void As (T* into) const
390 {
391 *into = this->As<T> ();
392 }
393 [[deprecated ("Since Stroika v3.0d4 use span")]] static BLOB Attach (const byte* start, const byte* end)
394 {
395 return Attach (span{start, end});
396 }
397 [[deprecated ("Since Stroika v3.0d4 use Attach")]] static BLOB AttachApplicationLifetime (const byte* start, const byte* end)
398 {
399 return Attach (span{start, end});
400 }
401 template <size_t SIZE>
402 [[deprecated ("Since Stroika v3.0d4 use Attach")]] static BLOB AttachApplicationLifetime (const byte (&data)[SIZE])
403 {
404 return Attach (span{data, SIZE});
405 }
406 template <typename... ARGS>
407 [[deprecated ("Since Stroika v3.0d5 use FromHex")]] static BLOB Hex (ARGS... args)
408 {
409 return FromHex (args...);
410 }
411 template <typename... ARGS>
412 [[deprecated ("Since Stroika v3.0d5 use FromRaw")]] static BLOB Raw (ARGS... args)
413 {
414 return FromRaw (args...);
415 }
416
417 private:
418 struct BasicRep_;
419 struct ZeroRep_;
420 struct AdoptRep_; // e.g. for static constexpr arrays
421 struct AdoptAndDeleteRep_; // for user allocated new byte[]....
422
423 private:
424 Stroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE Debug::AssertExternallySynchronizedChecker fThisAssertExternallySynchronized_;
425 shared_ptr<_IRep> fRep_;
426 };
427 static_assert (totally_ordered<BLOB>);
428 static_assert (regular<BLOB>);
429 static_assert (Common::explicitly_convertible_to<BLOB, span<const byte>>);
430
431 // @todo make this work
432 //static_assert (convertible_to<BLOB, span<byte>>);
433
434 /**
435 * This abstract interface defines the behavior of a BLOB.
436 */
437 struct BLOB::_IRep {
438 _IRep () = default;
439 _IRep (const _IRep&) = delete;
440 virtual ~_IRep () = default;
441 virtual span<const byte> GetBounds () const = 0;
442
443 nonvirtual const _IRep& operator= (const _IRep&) = delete;
444 };
445
446 inline namespace Literals {
447
448 /**
449 * @brief alias for BLOB::Attach - so treats argument as BLOB of bytes
450 *
451 * \par Example Usage
452 * \code
453 * BLOB b1 = "hello"_blob;
454 * Assert (b.size () == 5);
455 * Assert (b[0] == static_cast<byte> ('h'));
456 * \endcode
457 */
458 inline BLOB operator""_blob (const char* str, size_t len);
459
460 }
461
462}
463
464/*
465 ********************************************************************************
466 ***************************** Implementation Details ***************************
467 ********************************************************************************
468 */
469#include "BLOB.inl"
470
471#endif /*_Stroika_Foundation_Memory_BLOB_h_*/
#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
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual size_t length() const
Definition BLOB.inl:276
nonvirtual const byte * end() const
Definition BLOB.inl:263
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 byte operator[](const size_t i) const
Definition BLOB.inl:244
nonvirtual BLOB Slice(size_t startAt, size_t endAt) const
Definition BLOB.cpp:327
nonvirtual BLOB operator+(const BLOB &rhs) const
Definition BLOB.inl:311
nonvirtual bool empty() const
Definition BLOB.inl:251
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
static BLOB FromRaw(const T *s, const T *e)
Convert pointed to/referenced data to BLOB (treating the argument as raw bytes).
Definition BLOB.inl:150
nonvirtual size_t size() const
Definition BLOB.inl:286
nonvirtual const byte * data() const
Definition BLOB.inl:281
InputStream<>::Ptr is Smart pointer (with abstract Rep) class defining the interface to reading from ...
Concept: trivial shorthand for variadic same_as A or same_as B, or ...
Definition Concepts.h:122