/************************************************************************ * 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 .* ************************************************************************/ #include #include #include #include #include #include #include "libxisf.h" #include namespace LibXISF { static std::map 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 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, "F32Matrix"}, {Variant::Type::F64Matrix, "F64Matrix"}, }; template T fromChars(const char *beg, const char *end) { T val; std::from_chars(beg, end, val); return val; } template 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)) { val.real = std::stod(std::string(match[1].first, match[1].second)); val.imag = std::stod(std::string(match[1].first, match[1].second)); } return val; } template void fromCharsVector(Variant &v, size_t len, const ByteArray &data) { v = T(); v.value().resize(len); size_t size = len * sizeof(typename T::value_type); std::memcpy(&v.value()[0], data.data(), size); } template 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()(0, 0), data.data(), size); } template void toChars(const Variant &v, char *beg, char *end) { std::to_chars(beg, end, v.value()); } template void toCharsComplex(const Variant &v, std::string &str) { T complex = v.value(); std::stringstream ss; ss.imbue(std::locale("C")); ss << "(" << complex.real << "," << complex.imag << ")"; str = ss.str(); } template void toCharsVector(const Variant &v, size_t &len, ByteArray &data) { len = v.value().size(); size_t size = len * sizeof(typename T::value_type); data.resize(size); std::memcpy(data.data(), &v.value()[0], size); data.encodeBase64(); data.append('\0'); } template void toCharsMatrix(const Variant &v, size_t &rows, size_t &cols, ByteArray &data) { rows = v.value().rows(); cols = v.value().cols(); size_t size = rows * cols * sizeof(typename T::value_type); data.resize(size); std::memcpy(data.data(), &v.value()(0, 0), size); data.encodeBase64(); data.append('\0'); } 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) { if(!node.attribute("location")) variant.setValue(node.text().as_string()); else variant.setValue(String(data.constData(), data.size())); } else if(node.attribute("value")) { auto attr = node.attribute("value").value(); switch(typeId) { case Variant::Type::Int8: variant.setValue(fromChars(attr, attr + strlen(attr))); break; case Variant::Type::UInt8: variant.setValue(fromChars(attr, attr + strlen(attr))); break; case Variant::Type::Int16: variant.setValue(fromChars(attr, attr + strlen(attr))); break; case Variant::Type::UInt16: variant.setValue(fromChars(attr, attr + strlen(attr))); break; case Variant::Type::Int32: variant.setValue(fromChars(attr, attr + strlen(attr))); break; case Variant::Type::UInt32: variant.setValue(fromChars(attr, attr + strlen(attr))); break; case Variant::Type::Int64: variant.setValue(fromChars(attr, attr + strlen(attr))); break; case Variant::Type::UInt64: variant.setValue(fromChars(attr, attr + strlen(attr))); break; case Variant::Type::Float32: variant.setValue(node.attribute("value").as_float()); break; case Variant::Type::Float64: variant.setValue(node.attribute("value").as_double()); break; case Variant::Type::Complex32: variant.setValue(fromCharsComplex(attr, attr + strlen(attr))); break; case Variant::Type::Complex64: variant.setValue(fromCharsComplex(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(variant, len, data); break; case Variant::Type::UI8Vector: fromCharsVector(variant, len, data); break; case Variant::Type::I16Vector: fromCharsVector(variant, len, data); break; case Variant::Type::UI16Vector: fromCharsVector(variant, len, data); break; case Variant::Type::I32Vector: fromCharsVector(variant, len, data); break; case Variant::Type::UI32Vector: fromCharsVector(variant, len, data); break; case Variant::Type::I64Vector: fromCharsVector(variant, len, data); break; case Variant::Type::UI64Vector: fromCharsVector(variant, len, data); break; case Variant::Type::F32Vector: fromCharsVector(variant, len, data); break; case Variant::Type::F64Vector: fromCharsVector(variant, len, data); break; case Variant::Type::C32Vector: fromCharsVector(variant, len, data); break; case Variant::Type::C64Vector: fromCharsVector(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(variant, rows, cols, data); break; case Variant::Type::UI8Matrix: fromCharsMatrix(variant, rows, cols, data); break; case Variant::Type::I16Matrix: fromCharsMatrix(variant, rows, cols, data); break; case Variant::Type::UI16Matrix: fromCharsMatrix(variant, rows, cols, data); break; case Variant::Type::I32Matrix: fromCharsMatrix(variant, rows, cols, data); break; case Variant::Type::UI32Matrix: fromCharsMatrix(variant, rows, cols, data); break; case Variant::Type::I64Matrix: fromCharsMatrix(variant, rows, cols, data); break; case Variant::Type::UI64Matrix: fromCharsMatrix(variant, rows, cols, data); break; case Variant::Type::F32Matrix: fromCharsMatrix(variant, rows, cols, data); break; case Variant::Type::F64Matrix: fromCharsMatrix(variant, rows, cols, data); break; case Variant::Type::C32Matrix: fromCharsMatrix(variant, rows, cols, data); break; case Variant::Type::C64Matrix: fromCharsMatrix(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().c_str()); } else if(variant.type() == Variant::Type::Boolean) { node.append_attribute("value").set_value(variant.value() ? 1 : 0); } else if(variant.type() >= Variant::Type::Int8 && variant.type() <= Variant::Type::UInt64) { switch(variant.type()) { case Variant::Type::Int8: toChars(variant, str, end); break; case Variant::Type::UInt8: toChars(variant, str, end); break; case Variant::Type::Int16: toChars(variant, str, end); break; case Variant::Type::UInt16: toChars(variant, str, end); break; case Variant::Type::Int32: toChars(variant, str, end); break; case Variant::Type::UInt32: toChars(variant, str, end); break; case Variant::Type::Int64: toChars(variant, str, end); break; case Variant::Type::UInt64: toChars(variant, str, end); break; default: break; } node.append_attribute("value").set_value(str); } else if(variant.type() == Variant::Type::Float32) node.append_attribute("value").set_value(variant.value()); else if(variant.type() == Variant::Type::Float64) node.append_attribute("value").set_value(variant.value()); else if(variant.type() == Variant::Type::Complex32 || variant.type() == Variant::Type::Complex64) { std::string str; if(variant.type() == Variant::Type::Complex32) toCharsComplex(variant, str); else toCharsComplex(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(variant, len, data); break; case Variant::Type::UI8Vector: toCharsVector(variant, len, data); break; case Variant::Type::I16Vector: toCharsVector(variant, len, data); break; case Variant::Type::UI16Vector: toCharsVector(variant, len, data); break; case Variant::Type::I32Vector: toCharsVector(variant, len, data); break; case Variant::Type::UI32Vector: toCharsVector(variant, len, data); break; case Variant::Type::I64Vector: toCharsVector(variant, len, data); break; case Variant::Type::UI64Vector: toCharsVector(variant, len, data); break; case Variant::Type::F32Vector: toCharsVector(variant, len, data); break; case Variant::Type::F64Vector: toCharsVector(variant, len, data); break; case Variant::Type::C32Vector: toCharsVector(variant, len, data); break; case Variant::Type::C64Vector: toCharsVector(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(variant, rows, cols, data); break; case Variant::Type::UI8Matrix: toCharsMatrix(variant, rows, cols, data); break; case Variant::Type::I16Matrix: toCharsMatrix(variant, rows, cols, data); break; case Variant::Type::UI16Matrix: toCharsMatrix(variant, rows, cols, data); break; case Variant::Type::I32Matrix: toCharsMatrix(variant, rows, cols, data); break; case Variant::Type::UI32Matrix: toCharsMatrix(variant, rows, cols, data); break; case Variant::Type::I64Matrix: toCharsMatrix(variant, rows, cols, data); break; case Variant::Type::UI64Matrix: toCharsMatrix(variant, rows, cols, data); break; case Variant::Type::F32Matrix: toCharsMatrix(variant, rows, cols, data); break; case Variant::Type::F64Matrix: toCharsMatrix(variant, rows, cols, data); break; case Variant::Type::C32Matrix: toCharsMatrix(variant, rows, cols, data); break; case Variant::Type::C64Matrix: toCharsMatrix(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(), "%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(str.c_str(), str.c_str() + str.size()); break; case Variant::Type::Float32: variant = std::stof(str); break; case Variant::Type::Float64: variant = std::stod(str); 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 { int idx = _value.index(); 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; if(i != vector.back()) ss << ","; } ss << "}"; return ss.str(); }; auto matrixString = [](auto matrix) { std::stringstream ss; ss << "{"; for(int i=0; i(_value, str, end); break; case Variant::Type::UInt8: toChars(_value, str, end); break; case Variant::Type::Int16: toChars(_value, str, end); break; case Variant::Type::UInt16: toChars(_value, str, end); break; case Variant::Type::Int32: toChars(_value, str, end); break; case Variant::Type::UInt32: toChars(_value, str, end); break; case Variant::Type::Int64: toChars(_value, str, end); break; case Variant::Type::UInt64: toChars(_value, str, end); break; case Variant::Type::Float32: string = std::to_string(std::get(_value)); break; case Variant::Type::Float64: string = std::to_string(std::get(_value)); break; case Variant::Type::Complex32: toCharsComplex(_value, string); break; case Variant::Type::Complex64: toCharsComplex(_value, string); break; case Variant::Type::I8Vector: string = vectorString(std::get(_value)); break; case Variant::Type::UI8Vector: string = vectorString(std::get(_value)); break; case Variant::Type::I16Vector: string = vectorString(std::get(_value)); break; case Variant::Type::UI16Vector: string = vectorString(std::get(_value)); break; case Variant::Type::I32Vector: string = vectorString(std::get(_value)); break; case Variant::Type::UI32Vector: string = vectorString(std::get(_value)); break; case Variant::Type::I64Vector: string = vectorString(std::get(_value)); break; case Variant::Type::UI64Vector: string = vectorString(std::get(_value)); break; case Variant::Type::F32Vector: string = vectorString(std::get(_value)); break; case Variant::Type::F64Vector: string = vectorString(std::get(_value)); break; case Variant::Type::F32Matrix: string = matrixString(std::get(_value)); break; case Variant::Type::F64Matrix: string = matrixString(std::get(_value)); break; case Variant::Type::String: string = std::get(_value); break; case Variant::Type::TimePoint: { std::ostringstream ss; ss << std::put_time(&std::get(_value), "%Y-%m-%dT%H:%M:%SZ"); string = ss.str(); break; } default: string = typeName(); break; } if(type() >= Variant::Type::Int8 && type() <= Variant::Type::UInt64) string = str; return string; } }