Stroika Library 3.0d20
 
Loading...
Searching...
No Matches
BLOB.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Foundation/Containers/Common.h"
7#include "Stroika/Foundation/Memory/Common.h"
10
11namespace Stroika::Foundation::Memory {
12
13 template <>
14 Characters::String Stroika::Foundation::Memory::BLOB::AsBase64 () const;
15 template <>
16 Characters::String Stroika::Foundation::Memory::BLOB::AsBase64 (const Cryptography::Encoding::Algorithm::Base64::Options& o) const;
17
18 struct BLOB::BasicRep_ final : public _IRep, public Memory::UseBlockAllocationIfAppropriate<BasicRep_> {
19 // really not sure what size to use???
20 // May not be any universal, good answer...
21 // Remember - users can subclass BLOB, and provider their own
22 // 'rep' type tuned to their application.
23 InlineBuffer<byte, 64> fData;
24
25 BasicRep_ (span<const byte> s)
26 : fData{Memory::eUninitialized, s.size ()}
27 {
28 CopyBytes (s, span{fData});
29 }
30
31 BasicRep_ (const initializer_list<pair<const byte*, const byte*>>& startEndPairs);
32 BasicRep_ (const initializer_list<BLOB>& list2Concatenate);
33 BasicRep_ (const BasicRep_&) = delete;
34 BasicRep_& operator= (const BasicRep_&) = delete;
35
36 virtual span<const byte> GetBounds () const override;
37 };
38
39 struct BLOB::ZeroRep_ final : public _IRep, public Memory::UseBlockAllocationIfAppropriate<ZeroRep_> {
40 virtual span<const byte> GetBounds () const override;
41 ZeroRep_ () = default;
42 ZeroRep_ (const ZeroRep_&) = delete;
43 ZeroRep_& operator= (const ZeroRep_&) = delete;
44 };
45
46 struct BLOB::AdoptRep_ final : public _IRep, public Memory::UseBlockAllocationIfAppropriate<AdoptRep_> {
47 const byte* fStart;
48 const byte* fEnd;
49
50 AdoptRep_ (const AdoptRep_&) = delete;
51 AdoptRep_ (const byte* start, const byte* end);
52 ~AdoptRep_ () = default;
53 AdoptRep_& operator= (const AdoptRep_&) = delete;
54 virtual span<const byte> GetBounds () const override;
55 };
56
57 struct BLOB::AdoptAndDeleteRep_ final : public _IRep, public Memory::UseBlockAllocationIfAppropriate<AdoptAndDeleteRep_> {
58 const byte* fStart;
59 const byte* fEnd;
60
61 AdoptAndDeleteRep_ (const AdoptAndDeleteRep_&) = delete;
62 AdoptAndDeleteRep_ (const byte* start, const byte* end);
63 ~AdoptAndDeleteRep_ ();
64 AdoptAndDeleteRep_& operator= (const AdoptAndDeleteRep_&) = delete;
65 virtual span<const byte> GetBounds () const override;
66 };
67
68 /*
69 ********************************************************************************
70 ************************************** BLOB ************************************
71 ********************************************************************************
72 */
73 inline BLOB::BLOB ()
74 : fRep_{MakeSharedPtr<ZeroRep_> ()}
75 {
76 }
77 template <ranges::range CONTAINER_OF_BYTE>
78 inline BLOB::BLOB (const CONTAINER_OF_BYTE& data)
79 requires (is_convertible_v<typename CONTAINER_OF_BYTE::value_type, byte> or is_convertible_v<typename CONTAINER_OF_BYTE::value_type, uint8_t>)
80 : BLOB{as_bytes (span{data.data (), data.size ()})}
81 {
82 }
83 inline BLOB::BLOB (const initializer_list<byte>& bytes)
84 : BLOB{span{bytes}}
85 {
86 }
87 inline BLOB::BLOB (const initializer_list<uint8_t>& bytes)
88 : BLOB{as_bytes (span{bytes})}
89 {
90 }
91 inline BLOB::BLOB (span<const byte> s)
92 : fRep_{s.empty () ? shared_ptr<_IRep>{MakeSharedPtr<ZeroRep_> ()} : shared_ptr<_IRep>{MakeSharedPtr<BasicRep_> (s)}}
93 {
94 Ensure (s.size () == size ());
95 }
96 inline BLOB::BLOB (span<const uint8_t> s)
97 : BLOB{as_bytes (s)}
98 {
99 Ensure (s.size () == size ());
100 }
101 inline BLOB::BLOB (const byte* start, const byte* end)
102 : BLOB{span{start, end}}
103 {
104 Ensure (static_cast<size_t> (end - start) == size ());
105 }
106 inline BLOB::BLOB (const uint8_t* start, const uint8_t* end)
107 : BLOB{as_bytes (span{start, end})}
108 {
109 Ensure (static_cast<size_t> (end - start) == size ());
110 }
111 inline BLOB::BLOB (const initializer_list<pair<const byte*, const byte*>>& startEndPairs)
112 : fRep_{MakeSharedPtr<BasicRep_> (startEndPairs)}
113 {
114 }
115 inline BLOB::BLOB (const initializer_list<BLOB>& list2Concatenate)
116 : fRep_{MakeSharedPtr<BasicRep_> (list2Concatenate)}
117 {
118 }
119 inline BLOB::BLOB (const shared_ptr<_IRep>& rep)
120 : fRep_{rep}
121 {
122 }
123 inline BLOB::BLOB (shared_ptr<_IRep>&& rep)
124 : fRep_{move (rep)}
125 {
126 }
127 inline BLOB BLOB::FromHex (const char* b)
128 {
129 RequireNotNull (b);
130 return FromHex (span<const char>{b, ::strlen (b)});
131 }
132 inline BLOB BLOB::FromHex (const char* s, const char* e)
133 {
134 return FromHex (span<const char>{s, e});
135 }
136 inline BLOB BLOB::FromHex (string_view s)
137 {
138 return FromHex (span<const char>{s});
139 }
140 inline BLOB BLOB::FromBase64 (const char* b)
141 {
142 RequireNotNull (b);
143 return FromBase64 (span<const char>{b, ::strlen (b)});
144 }
145 inline BLOB BLOB::FromBase64 (string_view s)
146 {
147 return FromBase64 (span<const char>{s});
148 }
149 template <Common::trivially_copyable T>
150 inline BLOB BLOB::FromRaw (const T* s, const T* e)
151 {
152 return BLOB{span{reinterpret_cast<const byte*> (s), reinterpret_cast<const byte*> (e)}};
153 }
154 template <Common::trivially_copyable T>
155 inline BLOB BLOB::FromRaw (const T* s, size_t sz)
156 {
157 return BLOB{span{reinterpret_cast<const byte*> (s), sz}};
158 }
159 template <Common::trivially_copyable T>
160 inline BLOB BLOB::FromRaw (const T* s)
161 requires (same_as<typename char_traits<T>::char_type, T>)
162 {
163 RequireNotNull (s);
164 return FromRaw (s, s + char_traits<T>::length (s));
165 }
166 template <Common::trivially_copyable T>
167 inline BLOB BLOB::FromRaw (const basic_string<T>& s)
168 requires (same_as<typename char_traits<T>::char_type, T>)
169 {
170 return FromRaw (s.c_str (), s.c_str () + s.length ());
171 }
172 template <Common::trivially_copyable T>
173 inline BLOB BLOB::FromRaw (const T& s)
174 {
175 return FromRaw (&s, &s + 1);
176 }
177 template <typename BYTEISH, size_t EXTENT>
178 inline BLOB BLOB::Attach (span<BYTEISH, EXTENT> s)
179 requires (convertible_to<BYTEISH, const byte> or convertible_to<BYTEISH, const uint8_t>)
180 {
181 const byte* b = reinterpret_cast<const byte*> (s.data ());
182 return BLOB{MakeSharedPtr<AdoptRep_> (b, b + s.size ())};
183 }
184 template <typename BYTEISH, size_t EXTENT>
185 inline BLOB BLOB::Attach (BYTEISH (&data)[EXTENT])
186 requires (convertible_to<BYTEISH, const byte> or convertible_to<BYTEISH, const uint8_t>)
187 {
188 return Attach (span<const BYTEISH>{data, EXTENT});
189 }
190 inline BLOB BLOB::AttachAndDelete (const byte* b, size_t arrayLen)
191 {
192 return BLOB{MakeSharedPtr<AdoptAndDeleteRep_> (b, b + arrayLen)};
193 }
194 template <>
195 Streams::InputStream::Ptr<byte> BLOB::As () const;
196 template <typename T>
197 inline T BLOB::As () const
198#if !qCompilerAndStdLib_template_requires_doesnt_work_with_specialization_Buggy
199 // clang-format off
200 requires (
201 Common::IAnyOf<T,span<const byte>,span<const uint8_t>, pair<const byte*, const byte*>, pair<const uint8_t*, const uint8_t*>, vector<byte> ,vector<uint8_t>, Streams::InputStream::Ptr<byte>,string>
202 or is_trivially_copyable_v<T>
203 )
204 // clang-format on
205#endif
206 {
207 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
208 if constexpr (same_as<T, span<const byte>>) {
209 return fRep_->GetBounds ();
210 }
211 else if constexpr (same_as<T, span<const uint8_t>>) {
212 return T{reinterpret_cast<const uint8_t*> (this->data ()), this->size ()};
213 }
214 else if constexpr (same_as<T, pair<const byte*, const byte*>>) {
215 return make_pair (this->data (), this->data () + this->size ());
216 }
217 else if constexpr (same_as<T, pair<const uint8_t*, const uint8_t*>>) {
218 auto s = this->As<span<const uint8_t>> ();
219 return make_pair (s.data (), s.data () + s.size ());
220 }
221 else if constexpr (same_as<T, vector<byte>>) {
222 return T{this->begin (), this->end ()};
223 }
224 else if constexpr (same_as<T, vector<uint8_t>>) {
225 auto s = this->As<span<const uint8_t>> ();
226 return T{s.begin (), s.end ()};
227 }
228 else if constexpr (same_as<T, Streams::InputStream::Ptr<byte>>) {
229 AssertNotReached (); //template specialized - handled in C++ file
230 }
231 else if constexpr (same_as<T, string>) {
232 span<const byte> tmp = fRep_->GetBounds ();
233 return string{reinterpret_cast<const char*> (tmp.data ()), reinterpret_cast<const char*> (tmp.data () + tmp.size ())};
234 }
235 else if constexpr (is_trivially_copyable_v<T>) {
236 Require (size () >= sizeof (T)); // allow object slicing, but not reading garbage data
237 return *(reinterpret_cast<const T*> (begin ()));
238 }
239 }
240 inline BLOB::operator Streams::InputStream::Ptr<byte> () const
241 {
242 return As<Streams::InputStream::Ptr<byte>> ();
243 }
244 inline byte BLOB::operator[] (const size_t i) const
245 {
246 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
247 span<const byte> tmp = fRep_->GetBounds ();
248 Assert (i < tmp.size ());
249 return tmp[i];
250 }
251 inline bool BLOB::empty () const
252 {
253 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
254 span<const byte> tmp = fRep_->GetBounds ();
255 Assert (tmp.begin () <= tmp.end ());
256 return tmp.empty ();
257 }
258 inline const byte* BLOB::begin () const
259 {
260 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
261 return fRep_->GetBounds ().data ();
262 }
263 inline const byte* BLOB::end () const
264 {
265 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
266 auto b = fRep_->GetBounds ();
267 return b.data () + b.size ();
268 }
269 inline size_t BLOB::GetSize () const
270 {
271 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
272 span<const byte> tmp = fRep_->GetBounds ();
273 Assert (tmp.begin () <= tmp.end ());
274 return tmp.size ();
275 }
276 inline size_t BLOB::length () const
277 {
278 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
279 return GetSize ();
280 }
281 inline const byte* BLOB::data () const
282 {
283 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
284 return begin ();
285 }
286 inline size_t BLOB::size () const
287 {
288 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
289 return GetSize ();
290 }
291 inline strong_ordering BLOB::operator<=> (const BLOB& rhs) const
292 {
293 return TWC_ (*this, rhs);
294 }
295 inline bool BLOB::operator== (const BLOB& rhs) const
296 {
297 Debug::AssertExternallySynchronizedMutex::ReadContext readLockL{fThisAssertExternallySynchronized_}; // this pattern of double locking might risk a deadlock for real locks, but these locks are fake to assure externally locked
298 Debug::AssertExternallySynchronizedMutex::ReadContext readLockR{rhs.fThisAssertExternallySynchronized_};
299 if (fRep_ == rhs.fRep_) {
300 return true; // cheap optimization for not super uncommon case
301 }
302 span<const byte> l = fRep_->GetBounds ();
303 span<const byte> r = rhs.fRep_->GetBounds ();
304 size_t lSize = l.size ();
305 size_t rSize = r.size ();
306 if (lSize != rSize) {
307 return false;
308 }
309 return CompareBytes (l, r) == 0;
310 }
311 inline BLOB BLOB::operator+ (const BLOB& rhs) const
312 {
313 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
314 return BLOB ({*this, rhs});
315 }
316 inline strong_ordering BLOB::TWC_ (const BLOB& lhs, const BLOB& rhs)
317 {
318 Debug::AssertExternallySynchronizedMutex::ReadContext readLockL{lhs.fThisAssertExternallySynchronized_}; // this pattern of double locking might risk a deadlock for real locks, but these locks are fake to assure externally locked
319 Debug::AssertExternallySynchronizedMutex::ReadContext readLockR{rhs.fThisAssertExternallySynchronized_};
320 span<const byte> l = lhs.fRep_->GetBounds ();
321 span<const byte> r = rhs.fRep_->GetBounds ();
322 size_t lSize = l.size ();
323 size_t rSize = r.size ();
324 size_t nCommonBytes = min (lSize, rSize);
325 if (nCommonBytes != 0) {
326 // see http://stackoverflow.com/questions/16362925/can-i-pass-a-null-pointer-to-memcmp -- illegal to pass nullptr to memcmp() even if size 0
327 if (strong_ordering tmp = CompareBytes (l.subspan (0, nCommonBytes), r.subspan (0, nCommonBytes)); tmp != strong_ordering::equal) {
328 return tmp;
329 }
330 }
331 // if tmp is zero, and same size - its really zero. But if lhs shorter than right, say lhs < right
332 if (lSize == rSize) {
333 return strong_ordering::equal;
334 }
335 return (lSize < rSize) ? strong_ordering::less : strong_ordering::greater;
336 }
337
338 inline namespace Literals {
339 inline BLOB operator""_blob (const char* str, size_t len)
340 {
341 return BLOB::Attach (span<const byte>{reinterpret_cast<const byte*> (str), len});
342 }
343
344 }
345
346}
#define RequireNotNull(p)
Definition Assertions.h:347
#define AssertNotReached()
Definition Assertions.h:355
auto MakeSharedPtr(ARGS_TYPE &&... args) -> shared_ptr< T >
same as make_shared, but if type T has block allocation, then use block allocation for the 'shared pa...
shared_lock< const AssertExternallySynchronizedMutex > ReadContext
Instantiate AssertExternallySynchronizedMutex::ReadContext to designate an area of code where protect...
nonvirtual size_t length() const
Definition BLOB.inl:276
nonvirtual const byte * end() const
Definition BLOB.inl:263
nonvirtual STRING_TYPE AsBase64() const
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 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 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 ...