Files
libXISF/variant.cpp
T
2023-03-09 18:25:54 +01:00

459 lines
21 KiB
C++

/************************************************************************
* LibXISF - library to load and save XISF files *
* Copyright (C) 2023 Dušan Poizl *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>.*
************************************************************************/
#include <charconv>
#include <type_traits>
#include <map>
#include <regex>
#include <iomanip>
#include "libxisf.h"
#include "pugixml/pugixml.hpp"
namespace LibXISF
{
static std::map<std::string, Variant::Type> typeToId = {
{"Monostate", Variant::Type::Monostate},
{"Boolean", Variant::Type::Boolean},
{"Int8", Variant::Type::Int8},
{"UInt8", Variant::Type::UInt8},
{"Int16", Variant::Type::Int16},
{"UInt16", Variant::Type::UInt16},
{"Int32", Variant::Type::Int32},
{"UInt32", Variant::Type::UInt32},
{"Int64", Variant::Type::Int64},
{"UInt64", Variant::Type::UInt64},
{"Float32", Variant::Type::Float32},
{"Float64", Variant::Type::Float64},
{"Complex32", Variant::Type::Complex32},
{"Complex64", Variant::Type::Complex64},
{"String", Variant::Type::String},
{"TimePoint", Variant::Type::TimePoint},
{"I8Vector", Variant::Type::I8Vector},
{"UI8Vector", Variant::Type::UI8Vector},
{"I16Vector", Variant::Type::I16Vector},
{"UI16Vector", Variant::Type::UI16Vector},
{"I32Vector", Variant::Type::I32Vector},
{"UI32Vector", Variant::Type::UI32Vector},
{"I64Vector", Variant::Type::I64Vector},
{"UI64Vector", Variant::Type::UI64Vector},
{"F32Vector", Variant::Type::F32Vector},
{"F64Vector", Variant::Type::F64Vector},
{"C32Vector", Variant::Type::C32Vector},
{"C64Vector", Variant::Type::C64Vector},
{"I8Matrix", Variant::Type::I8Matrix},
{"UI8Matrix", Variant::Type::UI8Matrix},
{"I16Matrix", Variant::Type::I16Matrix},
{"UI16Matrix", Variant::Type::UI16Matrix},
{"I32Matrix", Variant::Type::I32Matrix},
{"UI32Matrix", Variant::Type::UI32Matrix},
{"I64Matrix", Variant::Type::I64Matrix},
{"UI64Matrix", Variant::Type::UI64Matrix},
{"F32Matrix", Variant::Type::F32Matrix},
{"F64Matrix", Variant::Type::F64Matrix},
{"C32Matrix", Variant::Type::C32Matrix},
{"C64Matrix", Variant::Type::C64Matrix},
};
static std::map<Variant::Type, const char*> idToType = {
{Variant::Type::Monostate, "Monostate"},
{Variant::Type::Boolean, "Boolean"},
{Variant::Type::Int8, "Int8"},
{Variant::Type::UInt8, "UInt8"},
{Variant::Type::Int16, "Int16"},
{Variant::Type::UInt16, "UInt16"},
{Variant::Type::Int32, "Int32"},
{Variant::Type::UInt32, "UInt32"},
{Variant::Type::Int64, "Int64"},
{Variant::Type::UInt64, "UInt64"},
{Variant::Type::Float32, "Float32"},
{Variant::Type::Float64, "Float64"},
{Variant::Type::Complex32, "Complex32"},
{Variant::Type::Complex64, "Complex64"},
{Variant::Type::String, "String"},
{Variant::Type::TimePoint, "TimePoint"},
{Variant::Type::I8Vector, "I8Vector"},
{Variant::Type::UI8Vector, "UI8Vector"},
{Variant::Type::I16Vector, "I16Vector"},
{Variant::Type::UI16Vector, "UI16Vector"},
{Variant::Type::I32Vector, "I32Vector"},
{Variant::Type::UI32Vector, "UI32Vector"},
{Variant::Type::I64Vector, "I64Vector"},
{Variant::Type::UI64Vector, "UI64Vector"},
{Variant::Type::F32Vector, "F32Vector"},
{Variant::Type::F64Vector, "F64Vector"},
{Variant::Type::C32Vector, "C32Vector"},
{Variant::Type::C64Vector, "C64Vector"},
{Variant::Type::I8Matrix, "I8Matrix"},
{Variant::Type::UI8Matrix, "UI8Matrix"},
{Variant::Type::I16Matrix, "I16Matrix"},
{Variant::Type::UI16Matrix, "UI16Matrix"},
{Variant::Type::I32Matrix, "I32Matrix"},
{Variant::Type::UI32Matrix, "UI32Matrix"},
{Variant::Type::I64Matrix, "I64Matrix"},
{Variant::Type::UI64Matrix, "UI64Matrix"},
{Variant::Type::F32Matrix, "I8Matrix"},
{Variant::Type::F64Matrix, "UI8Matrix"},
};
template<typename T>
T fromChars(const char *beg, const char *end)
{
T val;
std::from_chars(beg, end, val);
return val;
}
template<typename T>
T fromCharsComplex(const char *beg, const char *end)
{
T val = {0, 0};
std::cmatch match;
std::regex regex("\\(([^,]+),([^)]+)\\)");
if(std::regex_match(beg, end, match, regex))
{
std::from_chars(match[1].first, match[1].second, val.real);
std::from_chars(match[2].first, match[2].second, val.imag);
}
return val;
}
template<typename T>
void fromCharsVector(Variant &v, size_t len, const ByteArray &data)
{
v = T();
v.value<T>().resize(len);
size_t size = len * sizeof(typename T::value_type);
std::memcpy(&v.value<T>()[0], data.data(), size);
}
template<typename T>
void fromCharsMatrix(Variant &v, size_t rows, size_t cols, const ByteArray &data)
{
v = T(rows, cols);
size_t size = rows * cols * sizeof(typename T::value_type);
std::memcpy(&v.value<T>()(0, 0), data.data(), size);
}
template<typename T>
void toChars(const Variant &v, char *beg, char *end)
{
std::to_chars(beg, end, v.value<T>());
}
template<typename T>
void toCharsComplex(const Variant &v, std::string &str)
{
T complex = v.value<T>();
std::stringstream ss;
ss.imbue(std::locale("C"));
ss << "(" << complex.real << "," << complex.imag << ")";
str = ss.str();
}
template<typename T>
void toCharsVector(const Variant &v, size_t &len, ByteArray &data)
{
len = v.value<T>().size();
size_t size = len * sizeof(typename T::value_type);
data.resize(size);
std::memcpy(data.data(), &v.value<T>()[0], size);
data.encodeBase64();
}
template<typename T>
void toCharsMatrix(const Variant &v, size_t &rows, size_t &cols, ByteArray &data)
{
rows = v.value<T>().rows();
cols = v.value<T>().cols();
size_t size = rows * cols * sizeof(typename T::value_type);
data.resize(size);
std::memcpy(data.data(), &v.value<T>()(0, 0), size);
data.encodeBase64();
}
void deserializeVariant(const pugi::xml_node &node, Variant &variant, const ByteArray &data)
{
std::string type = node.attribute("type").as_string();
Variant::Type typeId = typeToId[type];
if(typeId == Variant::Type::String && !node.attribute("location"))
{
variant.setValue(node.text().as_string());
}
else if(node.attribute("value"))
{
auto attr = node.attribute("value").value();
switch(typeId)
{
case Variant::Type::Int8: variant.setValue(fromChars<Int8>(attr, attr + strlen(attr))); break;
case Variant::Type::UInt8: variant.setValue(fromChars<UInt8>(attr, attr + strlen(attr))); break;
case Variant::Type::Int16: variant.setValue(fromChars<Int16>(attr, attr + strlen(attr))); break;
case Variant::Type::UInt16: variant.setValue(fromChars<UInt16>(attr, attr + strlen(attr))); break;
case Variant::Type::Int32: variant.setValue(fromChars<Int32>(attr, attr + strlen(attr))); break;
case Variant::Type::UInt32: variant.setValue(fromChars<UInt32>(attr, attr + strlen(attr))); break;
case Variant::Type::Int64: variant.setValue(fromChars<Int64>(attr, attr + strlen(attr))); break;
case Variant::Type::UInt64: variant.setValue(fromChars<UInt64>(attr, attr + strlen(attr))); break;
case Variant::Type::Float32: variant.setValue(fromChars<Float32>(attr, attr + strlen(attr))); break;
case Variant::Type::Float64: variant.setValue(fromChars<Float64>(attr, attr + strlen(attr))); break;
case Variant::Type::Complex32: variant.setValue(fromCharsComplex<Complex32>(attr, attr + strlen(attr))); break;
case Variant::Type::Complex64: variant.setValue(fromCharsComplex<Complex64>(attr, attr + strlen(attr))); break;
case Variant::Type::TimePoint:
{
std::istringstream ss(node.attribute("value").value());
std::tm tm = {};
ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%SZ");
variant = tm;
break;
}
case Variant::Type::Boolean:
variant = node.attribute("value").as_bool();
break;
default: break;
}
}
else if(typeId >= Variant::Type::I8Vector && typeId <= Variant::Type::C64Vector)
{
size_t len = node.attribute("length").as_ullong();
switch(typeId)
{
case Variant::Type::I8Vector: fromCharsVector<I8Vector>(variant, len, data); break;
case Variant::Type::UI8Vector: fromCharsVector<UI8Vector>(variant, len, data); break;
case Variant::Type::I16Vector: fromCharsVector<I16Vector>(variant, len, data); break;
case Variant::Type::UI16Vector: fromCharsVector<UI16Vector>(variant, len, data); break;
case Variant::Type::I32Vector: fromCharsVector<I32Vector>(variant, len, data); break;
case Variant::Type::UI32Vector: fromCharsVector<UI32Vector>(variant, len, data); break;
case Variant::Type::I64Vector: fromCharsVector<I64Vector>(variant, len, data); break;
case Variant::Type::UI64Vector: fromCharsVector<UI64Vector>(variant, len, data); break;
case Variant::Type::F32Vector: fromCharsVector<F32Vector>(variant, len, data); break;
case Variant::Type::F64Vector: fromCharsVector<F64Vector>(variant, len, data); break;
case Variant::Type::C32Vector: fromCharsVector<C32Vector>(variant, len, data); break;
case Variant::Type::C64Vector: fromCharsVector<C64Vector>(variant, len, data); break;
default: break;
}
}
else if(typeId >= Variant::Type::I8Matrix && typeId <= Variant::Type::C64Matrix)
{
size_t rows = node.attribute("rows").as_ullong();
size_t cols = node.attribute("columns").as_ullong();
switch(typeId)
{
case Variant::Type::I8Matrix: fromCharsMatrix<I8Matrix>(variant, rows, cols, data); break;
case Variant::Type::UI8Matrix: fromCharsMatrix<UI8Matrix>(variant, rows, cols, data); break;
case Variant::Type::I16Matrix: fromCharsMatrix<I16Matrix>(variant, rows, cols, data); break;
case Variant::Type::UI16Matrix: fromCharsMatrix<UI16Matrix>(variant, rows, cols, data); break;
case Variant::Type::I32Matrix: fromCharsMatrix<I32Matrix>(variant, rows, cols, data); break;
case Variant::Type::UI32Matrix: fromCharsMatrix<UI32Matrix>(variant, rows, cols, data); break;
case Variant::Type::I64Matrix: fromCharsMatrix<I64Matrix>(variant, rows, cols, data); break;
case Variant::Type::UI64Matrix: fromCharsMatrix<UI64Matrix>(variant, rows, cols, data); break;
case Variant::Type::F32Matrix: fromCharsMatrix<F32Matrix>(variant, rows, cols, data); break;
case Variant::Type::F64Matrix: fromCharsMatrix<F64Matrix>(variant, rows, cols, data); break;
case Variant::Type::C32Matrix: fromCharsMatrix<C32Matrix>(variant, rows, cols, data); break;
case Variant::Type::C64Matrix: fromCharsMatrix<C64Matrix>(variant, rows, cols, data); break;
default: break;
}
}
}
void serializeVariant(pugi::xml_node &node, const Variant &variant)
{
char str[32] = {0};
char *end = str + sizeof(str);
node.append_attribute("type").set_value(variant.typeName());
if(variant.type() == Variant::Type::String)
{
node.append_child(pugi::node_pcdata).set_value(variant.value<String>().c_str());
}
else if(variant.type() == Variant::Type::Boolean)
{
node.append_attribute("value").set_value(variant.value<Boolean>() ? 1 : 0);
}
else if(variant.type() >= Variant::Type::Int8 && variant.type() <= Variant::Type::Float64)
{
switch(variant.type())
{
case Variant::Type::Int8: toChars<Int8>(variant, str, end); break;
case Variant::Type::UInt8: toChars<UInt8>(variant, str, end); break;
case Variant::Type::Int16: toChars<Int16>(variant, str, end); break;
case Variant::Type::UInt16: toChars<UInt16>(variant, str, end); break;
case Variant::Type::Int32: toChars<Int32>(variant, str, end); break;
case Variant::Type::UInt32: toChars<UInt32>(variant, str, end); break;
case Variant::Type::Int64: toChars<Int64>(variant, str, end); break;
case Variant::Type::UInt64: toChars<UInt64>(variant, str, end); break;
case Variant::Type::Float32: toChars<Float32>(variant, str, end); break;
case Variant::Type::Float64: toChars<Float64>(variant, str, end); break;
default: break;
}
node.append_attribute("value").set_value(str);
}
else if(variant.type() == Variant::Type::Complex32 || variant.type() == Variant::Type::Complex64)
{
std::string str;
if(variant.type() == Variant::Type::Complex32)
toCharsComplex<Complex32>(variant, str);
else
toCharsComplex<Complex64>(variant, str);
node.append_attribute("value").set_value(str.c_str());
}
else if(variant.type() >= Variant::Type::I8Vector && variant.type() <= Variant::Type::C64Vector)
{
size_t len = 0;
ByteArray data;
switch(variant.type())
{
case Variant::Type::I8Vector: toCharsVector<I8Vector>(variant, len, data); break;
case Variant::Type::UI8Vector: toCharsVector<UI8Vector>(variant, len, data); break;
case Variant::Type::I16Vector: toCharsVector<I16Vector>(variant, len, data); break;
case Variant::Type::UI16Vector: toCharsVector<UI16Vector>(variant, len, data); break;
case Variant::Type::I32Vector: toCharsVector<I32Vector>(variant, len, data); break;
case Variant::Type::UI32Vector: toCharsVector<UI32Vector>(variant, len, data); break;
case Variant::Type::I64Vector: toCharsVector<I64Vector>(variant, len, data); break;
case Variant::Type::UI64Vector: toCharsVector<UI64Vector>(variant, len, data); break;
case Variant::Type::F32Vector: toCharsVector<F32Vector>(variant, len, data); break;
case Variant::Type::F64Vector: toCharsVector<F64Vector>(variant, len, data); break;
case Variant::Type::C32Vector: toCharsVector<C32Vector>(variant, len, data); break;
case Variant::Type::C64Vector: toCharsVector<C64Vector>(variant, len, data); break;
default: break;
}
node.append_attribute("length").set_value(len);
node.append_attribute("location").set_value("inline:base64");
node.append_child(pugi::node_pcdata).set_value(data.constData());
}
else if(variant.type() >= Variant::Type::I8Matrix && variant.type() <= Variant::Type::C64Matrix)
{
size_t rows = 0;
size_t cols = 0;
ByteArray data;
switch(variant.type())
{
case Variant::Type::I8Matrix: toCharsMatrix<I8Matrix>(variant, rows, cols, data); break;
case Variant::Type::UI8Matrix: toCharsMatrix<UI8Matrix>(variant, rows, cols, data); break;
case Variant::Type::I16Matrix: toCharsMatrix<I16Matrix>(variant, rows, cols, data); break;
case Variant::Type::UI16Matrix: toCharsMatrix<UI16Matrix>(variant, rows, cols, data); break;
case Variant::Type::I32Matrix: toCharsMatrix<I32Matrix>(variant, rows, cols, data); break;
case Variant::Type::UI32Matrix: toCharsMatrix<UI32Matrix>(variant, rows, cols, data); break;
case Variant::Type::I64Matrix: toCharsMatrix<I64Matrix>(variant, rows, cols, data); break;
case Variant::Type::UI64Matrix: toCharsMatrix<UI64Matrix>(variant, rows, cols, data); break;
case Variant::Type::F32Matrix: toCharsMatrix<F32Matrix>(variant, rows, cols, data); break;
case Variant::Type::F64Matrix: toCharsMatrix<F64Matrix>(variant, rows, cols, data); break;
case Variant::Type::C32Matrix: toCharsMatrix<C32Matrix>(variant, rows, cols, data); break;
case Variant::Type::C64Matrix: toCharsMatrix<C64Matrix>(variant, rows, cols, data); break;
default: break;
}
node.append_attribute("rows").set_value(rows);
node.append_attribute("columns").set_value(cols);
node.append_attribute("location").set_value("inline:base64");
node.append_child(pugi::node_pcdata).set_value(data.constData());
}
else if(variant.type() == Variant::Type::TimePoint)
{
std::ostringstream ss;
ss << std::put_time(&variant.value<TimePoint>(), "%Y-%m-%dT%H:%M:%SZ");
node.append_attribute("value").set_value(ss.str().c_str());
}
}
Variant variantFromString(Variant::Type type, const String &str)
{
Variant variant;
switch(type)
{
case Variant::Type::Int32: variant = fromChars<Int32>(str.c_str(), str.c_str() + str.size()); break;
case Variant::Type::Float32: variant = fromChars<Float32>(str.c_str(), str.c_str() + str.size()); break;
case Variant::Type::Float64: variant = fromChars<Float64>(str.c_str(), str.c_str() + str.size()); break;
case Variant::Type::String: variant = str; break;
case Variant::Type::TimePoint:
{
std::istringstream ss(str);
std::tm tm = {};
ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%SZ");
variant = tm;
break;
}
default: break;
}
return variant;
}
Variant::Type Variant::type() const
{
return (Variant::Type)_value.index();
}
const char* Variant::typeName() const
{
if(idToType.count(type()))
return idToType[type()];
else
return "";
}
String Variant::toString() const
{
std::string string;
char str[32] = {0};
char *end = str + sizeof(str);
auto vectorString = [](auto &vector) {
std::stringstream ss;
ss << "{";
for(auto &i : vector)
ss << i << ",";
ss << "}";
return ss.str();
};
switch(type())
{
case Variant::Type::Int8: toChars<Int8>(_value, str, end); break;
case Variant::Type::UInt8: toChars<UInt8>(_value, str, end); break;
case Variant::Type::Int16: toChars<Int16>(_value, str, end); break;
case Variant::Type::UInt16: toChars<UInt16>(_value, str, end); break;
case Variant::Type::Int32: toChars<Int32>(_value, str, end); break;
case Variant::Type::UInt32: toChars<UInt32>(_value, str, end); break;
case Variant::Type::Int64: toChars<Int64>(_value, str, end); break;
case Variant::Type::UInt64: toChars<UInt64>(_value, str, end); break;
case Variant::Type::Float32: toChars<Float32>(_value, str, end); break;
case Variant::Type::Float64: toChars<Float64>(_value, str, end); break;
case Variant::Type::Complex32: toCharsComplex<Complex32>(_value, string); break;
case Variant::Type::Complex64: toCharsComplex<Complex64>(_value, string); break;
case Variant::Type::I8Vector: string = vectorString(std::get<I8Vector>(_value)); break;
case Variant::Type::UI8Vector: string = vectorString(std::get<UI8Vector>(_value)); break;
case Variant::Type::I16Vector: string = vectorString(std::get<I16Vector>(_value)); break;
case Variant::Type::UI16Vector: string = vectorString(std::get<UI16Vector>(_value)); break;
case Variant::Type::I32Vector: string = vectorString(std::get<I32Vector>(_value)); break;
case Variant::Type::UI32Vector: string = vectorString(std::get<UI32Vector>(_value)); break;
case Variant::Type::I64Vector: string = vectorString(std::get<I64Vector>(_value)); break;
case Variant::Type::UI64Vector: string = vectorString(std::get<UI64Vector>(_value)); break;
case Variant::Type::F32Vector: string = vectorString(std::get<F32Vector>(_value)); break;
case Variant::Type::F64Vector: string = vectorString(std::get<F64Vector>(_value)); break;
case Variant::Type::String: string = std::get<String>(_value); break;
default: string = typeName(); break;
}
if(type() >= Variant::Type::Int8 && type() <= Variant::Type::Float64)
string = str;
return string;
}
}