G+Smo  25.01.0
Geometry + Simulation Modules
 
Loading...
Searching...
No Matches
gsBase64.h
1#pragma once
2
3#include <gsCore/gsDebug.h>
4#include <gsMatrix/gsMatrix.h>
5
6#include <algorithm>
7#include <array>
8#include <numeric>
9#include <string>
10#include <vector>
11
12namespace gismo {
13
21class GISMO_EXPORT Base64 {
22 private:
24 using ByteRepresentation = unsigned char;
25
27 static char char_encode_table(const unsigned& index) {
28 // Create static array in function to avoid use of c++17 static member
29 // declaration
30 static const std::array<char, 64> encode_table{
31 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
32 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
33 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
34 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
35 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
36 GISMO_ASSERT((index < 64),
37 "Requested index out of range. Input invalid");
38 return encode_table[index];
39 }
40
49 static const std::array<unsigned, 256> ReverseCharEncodeTable_() {
50 std::array<unsigned, 256> et_reversed{};
51 // Fill remainding array entries with values outside range to throw
52 // assertions easily
53 et_reversed.fill(256);
54 const unsigned n_chars_{64};
55 for (unsigned i{}; i < n_chars_; i++) {
56 et_reversed[static_cast<unsigned>(char_encode_table(i))] = i;
57 }
58 return et_reversed;
59 }
60
62 static unsigned char_decode_table(const unsigned& index) {
63 static const std::array<unsigned, 256> & decode_table =
64 ReverseCharEncodeTable_();
65 GISMO_ASSERT((index < 256),
66 "Requested index out of range. Input invalid");
67 GISMO_ASSERT((decode_table[index] != 256),
68 "Invalid decode type, this should never occur!");
69 return decode_table[index];
70 }
71
72 // Check if string fulfills minimum requirements for B64 encoded strings
73 static bool isValidBase64String(const std::string& s) {
74 // Check if size is feasible
75 bool is_valid{s.size() % 4 == 0};
76 // Check if string only contains alpha-numeric chars or `/`, `+` or `=`
77 return is_valid && std::all_of(s.begin(), s.end(), [](const char& c) {
78 return isalnum(c) || c == '+' || c == '/' || c == '=';
79 });
80 }
81
88 static std::string trimWhitespaces(const std::string& s) {
89 const std::string delimiters(" \n\t");
90 size_t first = s.find_first_not_of(delimiters);
91 if (std::string::npos == first) {
92 GISMO_ERROR("Empty string cannot be converted into data-vector");
93 }
94 size_t last = s.find_last_not_of(delimiters);
95 return s.substr(first, (last - first + 1));
96 }
97
106 static std::string Encode_(const ByteRepresentation* byte_vector_ptr,
107 const std::size_t& minimum_n_bytes_required) {
108 // Padding blocks
109 const std::size_t additional_padding_bytes =
110 (3 - minimum_n_bytes_required % 3) % 3;
111
112 // Required groups of three
113 const std::size_t number_of_groups =
114 (minimum_n_bytes_required + additional_padding_bytes) / 3;
115
116 // Initialize return value
117 std::string encoded_string;
118 encoded_string.reserve(number_of_groups * 4);
119
120 // Loop over bytes and decode them
121 for (std::size_t i_group{}; i_group < number_of_groups; i_group++) {
122 const std::size_t buffer_index = i_group * 3;
123 std::array<ByteRepresentation, 3> buffer{};
124 buffer[0] = buffer_index < minimum_n_bytes_required
125 ? byte_vector_ptr[buffer_index + 0]
126 : 0;
127 buffer[1] = buffer_index < minimum_n_bytes_required
128 ? byte_vector_ptr[buffer_index + 1]
129 : 0;
130 buffer[2] = buffer_index < minimum_n_bytes_required
131 ? byte_vector_ptr[buffer_index + 2]
132 : 0;
133
134 // Transform bytes into chars using above private encoder table
135 encoded_string.push_back(
136 char_encode_table(((buffer[0] & 0xfc) >> 2)));
137 encoded_string.push_back(char_encode_table(
138 ((buffer[0] & 0x03) << 4) + ((buffer[1] & 0xf0) >> 4)));
139 encoded_string.push_back(char_encode_table(
140 ((buffer[1] & 0x0f) << 2) + ((buffer[2] & 0xc0) >> 6)));
141 encoded_string.push_back(
142 char_encode_table(((buffer[2] & 0x3f) << 0)));
143 }
144
145 // Replace trailing invalid data with `=`
146 for (size_t i = 0; i < additional_padding_bytes; ++i) {
147 encoded_string[number_of_groups * 4 - i - 1] = '=';
148 }
149
150 // Safety check
152 isValidBase64String(encoded_string),
153 "Something went wrong in B64 encoding, please write an issue");
154 return encoded_string;
155 }
156
171 template <typename BaseType, typename TargetType>
172 static void CopyIntoGsMatrix(const std::vector<BaseType>& base_vector,
173 gsMatrix<TargetType>& result) {
174 // Check for size
175 const unsigned rows = result.rows();
176 const unsigned cols = result.cols();
177 // Size check
178 if (base_vector.size() != (rows * cols)) {
180 "Input array has the wrong size or could not be converted");
181 }
182 // Converting into gsMatrix (manipulating directly on gsMatrix is
183 // more efficient if T=InputType)
184 for (unsigned i = 0; i < rows; ++i) {
185 for (unsigned j = 0; j < cols; ++j) {
186 result(i, j) =
187 static_cast<TargetType>(base_vector[i * cols + j]);
188 }
189 }
190 }
191
200 template <typename BaseType, typename TargetType>
201 static void CopyIntoVector(const std::vector<BaseType>& base_vector,
202 std::vector<TargetType>& result) {
203 std::transform(base_vector.cbegin(), base_vector.cend(),
204 std::back_inserter(result), [](const BaseType& c) {
205 return static_cast<TargetType>(c);
206 });
207 }
208
209 public:
217 template <typename BaseType>
218 static std::string Encode(const std::vector<BaseType>& data_vector) {
219 const ByteRepresentation* vector_as_bytes_ptr =
220 reinterpret_cast<const ByteRepresentation*>(&data_vector[0]);
221
222 // Number of bytes for an entry
223 constexpr const std::size_t length_of_entry{sizeof(BaseType{})};
224 // Minimum number of bytes required
225 const std::size_t minimum_n_bytes_required =
226 length_of_entry * data_vector.size();
227
228 return Encode_(vector_as_bytes_ptr, minimum_n_bytes_required);
229 }
230
239 template <typename BaseType>
240 static std::string Encode(const gsMatrix<BaseType>& data_vector,
241 const bool& row_wise = true) {
242 GISMO_ASSERT(std::is_arithmetic<BaseType>::value, // can be static
243 "Encoding is unsafe for non-arithmetic types.");
244 // We need to ensure that the export is in the demanded export order, if
245 // the data is only a vector (i.e. either col or row is 1) or if the
246 // storage scheme is coherent with the demanded export order
247 if ((data_vector.cols() == 1) || (data_vector.rows() == 1) ||
248 (gsMatrix<BaseType>::IsRowMajor == row_wise)) {
249 const ByteRepresentation* vector_as_bytes_ptr =
250 reinterpret_cast<const ByteRepresentation*>(data_vector.data());
251
252 // Number of bytes for an entry
253 constexpr const std::size_t length_of_entry{sizeof(BaseType{})};
254 // Minimum number of bytes required
255 const std::size_t minimum_n_bytes_required =
256 length_of_entry * data_vector.size();
257
258 return Encode_(vector_as_bytes_ptr, minimum_n_bytes_required);
259 } else {
260 // Here we need to flip the order of elements, for simplicity the
261 // data is copied into a temporary vector (works best with current
262 // implementation but might be slower than other approaches)
263 std::vector<BaseType> copy_of_matrix;
264 copy_of_matrix.reserve(data_vector.rows() * data_vector.cols());
265
266 // For readability we manipulate the Matrix using (memory safe)
267 // operator() overloads
268 if (row_wise) {
269 for (index_t i = 0; i < data_vector.rows(); ++i) {
270 for (index_t j = 0; j < data_vector.cols(); ++j) {
271 copy_of_matrix.push_back(data_vector(i, j));
272 }
273 }
274 } else {
275 for (index_t j = 0; j < data_vector.cols(); ++j) {
276 for (index_t i = 0; i < data_vector.rows(); ++i) {
277 copy_of_matrix.push_back(data_vector(i, j));
278 }
279 }
280 }
281 // Use vector overload
282 return Encode(copy_of_matrix);
283 }
284 }
285
294 template <typename OutputType>
295 static std::vector<OutputType> Decode(const std::string& base64string) {
296 // Safeguard
297 const std::string& base64string_trimmed = trimWhitespaces(base64string);
298 // Check validity of string
299 GISMO_ASSERT(isValidBase64String(base64string_trimmed),
300 "Validity check failed");
301
302 // Init return value
303 const std::size_t number_of_groups{base64string_trimmed.size() / 4};
304 constexpr const std::size_t length_of_entry{sizeof(OutputType{})};
305 const std::size_t number_of_output_values{(number_of_groups * 3) /
306 length_of_entry};
307 std::vector<OutputType> return_value;
308 return_value.resize(number_of_output_values);
309
310 // Access as byte stream
311 ByteRepresentation* vector_as_bytes =
312 reinterpret_cast<ByteRepresentation*>(&return_value[0]);
313
314 // Start the reverse process
315 for (std::size_t i_group{}; i_group < number_of_groups; i_group++) {
316 const std::size_t buffer_index = i_group * 4;
317 std::array<unsigned, 4> buffer{};
318 for (unsigned i{}; i < 4; i++) {
319 buffer[i] = base64string_trimmed[buffer_index + i] != '='
320 ? char_decode_table(static_cast<unsigned>(
321 base64string_trimmed[buffer_index + i]))
322 : 255;
323 }
324
325 // Write bytes into vector
326 if (buffer[1] != 255) {
327 vector_as_bytes[i_group * 3] =
328 ((buffer[0] & 0x3f) << 2) + ((buffer[1] & 0x30) >> 4);
329 }
330 if (buffer[2] != 255) {
331 vector_as_bytes[i_group * 3 + 1] =
332 ((buffer[1] & 0x0f) << 4) + ((buffer[2] & 0x3c) >> 2);
333 }
334 if (buffer[3] != 255) {
335 vector_as_bytes[i_group * 3 + 2] =
336 ((buffer[2] & 0x03) << 6) + ((buffer[3] & 0x3f) >> 0);
337 }
338 }
339 return return_value;
340 }
341
354 template <typename ScalarType>
355 static void DecodeIntoGsType(const std::string& base64_string,
356 const std::string& base_type_flag_,
357 gsMatrix<ScalarType>& result) {
358 // Format flag in this function is case sensitive
360 std::none_of(base_type_flag_.begin(), base_type_flag_.end(),
361 isupper),
362 "Format flag {ascii, b64float64, ...} must be all lowercase.");
363
364 // Perform type checks (no integral to floting point conversion)
365 if (std::is_integral<ScalarType>::value ^
366 (base_type_flag_.find("int") != std::string::npos)) {
368 "Conversions from integral to floating type and vice-versa is "
369 "not allowed!");
370 }
371
372 // Perform the actual input (using the proper encoding type)
373 if (base_type_flag_ == "b64uint16") { // Unsigned int 16
374 CopyIntoGsMatrix(Decode<uint16_t>(base64_string), result);
375 } else if (base_type_flag_ == "b64uint32") { // Unsigned int 32
376 CopyIntoGsMatrix(Decode<uint32_t>(base64_string), result);
377 } else if (base_type_flag_ == "b64bint64") { // Unsigned int 64
378 CopyIntoGsMatrix(Decode<uint64_t>(base64_string), result);
379 } else if (base_type_flag_ == "b64int16") { // Int 16
380 CopyIntoGsMatrix(Base64::Decode<int16_t>(base64_string), result);
381 } else if (base_type_flag_ == "b64int32") { // Int 32
382 CopyIntoGsMatrix(Base64::Decode<int32_t>(base64_string), result);
383 } else if (base_type_flag_ == "b64int64") { // Int 64
384 CopyIntoGsMatrix(Base64::Decode<int64_t>(base64_string), result);
385 } else if (base_type_flag_ == "b64float32") { // Float 32
386 CopyIntoGsMatrix(Base64::Decode<float>(base64_string), result);
387 } else if (base_type_flag_ == "b64float64") { // Float 64
388 CopyIntoGsMatrix(Base64::Decode<double>(base64_string), result);
389 } else {
390 GISMO_ERROR("Reading matrix from XML found unknown type");
391 }
392 }
393
406 template <typename ScalarType>
407 static void DecodeIntoGsType(const std::string& base64_string,
408 const std::string& base_type_flag_,
409 std::vector<ScalarType>& result) {
410 // Format flag in this function is case sensitive
412 std::none_of(base_type_flag_.begin(), base_type_flag_.end(),
413 isupper),
414 "Format flag {ascii, b64float64, ...} must be all lowercase.");
415
416 // Perform type checks (no integral to floting point conversion)
417 if (std::is_integral<ScalarType>::value ^
418 (base_type_flag_.find("int") != std::string::npos)) {
420 "Conversions from integral to floating type and vice-versa is "
421 "not allowed!");
422 }
423
424 // Perform the actual input (using the proper encoding type)
425 if (base_type_flag_ == "b64uint16") { // Unsigned int 16
426 CopyIntoVector(Decode<uint16_t>(base64_string), result);
427 } else if (base_type_flag_ == "b64uint32") { // Unsigned int 32
428 CopyIntoVector(Decode<uint32_t>(base64_string), result);
429 } else if (base_type_flag_ == "b64bint64") { // Unsigned int 64
430 CopyIntoVector(Decode<uint64_t>(base64_string), result);
431 } else if (base_type_flag_ == "b64int16") { // Int 16
432 CopyIntoVector(Base64::Decode<int16_t>(base64_string), result);
433 } else if (base_type_flag_ == "b64int32") { // Int 32
434 CopyIntoVector(Base64::Decode<int32_t>(base64_string), result);
435 } else if (base_type_flag_ == "b64int64") { // Int 64
436 CopyIntoVector(Base64::Decode<int64_t>(base64_string), result);
437 } else if (base_type_flag_ == "b64float32") { // Float 32
438 CopyIntoVector(Base64::Decode<float>(base64_string), result);
439 } else if (base_type_flag_ == "b64float64") { // Float 64
440 CopyIntoVector(Base64::Decode<double>(base64_string), result);
441 } else {
442 GISMO_ERROR("Reading matrix from XML found unknown type");
443 }
444 }
445};
446} // namespace gismo
Encode for base64 export.
Definition gsBase64.h:21
static unsigned char_decode_table(const unsigned &index)
Lookup Table for Decoding B64 string.
Definition gsBase64.h:62
static std::string Encode_(const ByteRepresentation *byte_vector_ptr, const std::size_t &minimum_n_bytes_required)
Actual encoding routine where byte-stream is transformed and encoded.
Definition gsBase64.h:106
static std::string Encode(const std::vector< BaseType > &data_vector)
Helper routine for std::vector data.
Definition gsBase64.h:218
static void CopyIntoGsMatrix(const std::vector< BaseType > &base_vector, gsMatrix< TargetType > &result)
Copy a read vector of type BaseType into a gsMatrix with ScalarType TargetType.
Definition gsBase64.h:172
static const std::array< unsigned, 256 > ReverseCharEncodeTable_()
Reverse the encoding table in an array.
Definition gsBase64.h:49
static void CopyIntoVector(const std::vector< BaseType > &base_vector, std::vector< TargetType > &result)
Cast a vector of a base type into a vector of TargetType.
Definition gsBase64.h:201
static std::string Encode(const gsMatrix< BaseType > &data_vector, const bool &row_wise=true)
Helper routine for gsMatrix Types (non-sparse)
Definition gsBase64.h:240
static void DecodeIntoGsType(const std::string &base64_string, const std::string &base_type_flag_, gsMatrix< ScalarType > &result)
Decode a string and copy into requested gismo Type.
Definition gsBase64.h:355
unsigned char ByteRepresentation
Alias for one byte type.
Definition gsBase64.h:24
static char char_encode_table(const unsigned &index)
Look up table.
Definition gsBase64.h:27
static std::vector< OutputType > Decode(const std::string &base64string)
Reading a B64 string, transforming it into a vector of a specific type.
Definition gsBase64.h:295
static void DecodeIntoGsType(const std::string &base64_string, const std::string &base_type_flag_, std::vector< ScalarType > &result)
Decode a string and copy into requested gismo Type.
Definition gsBase64.h:407
static std::string trimWhitespaces(const std::string &s)
Trim trailing and preceding whitespaces.
Definition gsBase64.h:88
A matrix with arbitrary coefficient type and fixed or dynamic size.
Definition gsMatrix.h:41
#define index_t
Definition gsConfig.h:32
This file contains the debugging and messaging system of G+Smo.
#define GISMO_ERROR(message)
Definition gsDebug.h:118
#define GISMO_ASSERT(cond, message)
Definition gsDebug.h:89
Provides declaration of Matrix class.
The G+Smo namespace, containing all definitions for the library.