You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

823 lines
24 KiB

#pragma once
#include "merrors.h"
#include <utility>
#include <variant>
using michlib::Error;
using michlib::int1;
using michlib::int2;
using michlib::int4;
using michlib::int8;
using michlib::int_cast;
using michlib::MString;
using michlib::RetVal;
using michlib::uint1;
using michlib::uint8;
class NcZarrTypes
{
protected:
using AttVT = std::variant<std::monostate, int8, uint8, double, MString, bool>;
class ArrCounter
{
using VT = std::vector<size_t>;
const VT count;
VT ind;
bool end;
public:
static size_t Index(const VT& i, const VT& c)
{
size_t out = 0;
size_t mul = 1;
for(size_t ii = i.size(); ii != 0; ii--)
{
out += mul * i[ii - 1];
mul *= c[ii - 1];
}
return out;
}
static VT Index(size_t lind, const VT& c)
{
VT out(c.size());
size_t j = lind;
for(auto i = c.size(); i > 0; i--)
{
out[i - 1] = j % c[i - 1];
j = j / c[i - 1];
}
return out;
}
ArrCounter() = delete;
ArrCounter(const VT& cnt): count(cnt), ind(cnt.size(), 0), end(false) {}
size_t operator[](size_t i) const { return ind[i]; }
ArrCounter& operator++()
{
size_t curind = count.size();
while(curind != 0)
{
ind[curind - 1]++;
if(ind[curind - 1] >= count[curind - 1])
{
ind[curind - 1] = 0;
curind--;
}
else
return *this;
}
ind = count;
end = true;
return *this;
}
explicit operator bool() const { return !end; }
size_t Index() const { return Index(ind, count); }
size_t Index(const VT& i) const { return Index(i, count); }
VT Index(size_t lind) const { return Index(lind, count); }
size_t Count(size_t i) const { return count[i]; }
const VT& VIndex() const { return ind; }
VT VIndex(const VT& start) const
{
VT out(ind.size());
for(size_t i = 0; i < ind.size(); i++) out[i] = ind[i] + start[i];
return out;
}
const auto& Count() const { return count; }
size_t N() const
{
size_t out = 1;
for(size_t i = 0; i < count.size(); i++) out *= count[i];
return out;
}
};
public:
enum class AttType
{
UNDEF,
INT,
UINT,
REAL,
STRING,
BOOL
};
enum class VarType
{
UNDEF,
FLOAT,
DOUBLE,
INT1,
INT2,
INT4,
INT8,
UINT1
};
protected:
template<VarType VT, class Dummy = void> struct VarType2Type;
template<class Dummy> struct VarType2Type<VarType::FLOAT, Dummy>
{
using type = float;
};
template<class Dummy> struct VarType2Type<VarType::DOUBLE, Dummy>
{
using type = double;
};
template<class Dummy> struct VarType2Type<VarType::INT1, Dummy>
{
using type = int1;
};
template<class Dummy> struct VarType2Type<VarType::INT2, Dummy>
{
using type = int2;
};
template<class Dummy> struct VarType2Type<VarType::INT4, Dummy>
{
using type = int4;
};
template<class Dummy> struct VarType2Type<VarType::INT8, Dummy>
{
using type = int8;
};
template<class Dummy> struct VarType2Type<VarType::UINT1, Dummy>
{
using type = uint1;
};
template<VarType VT> using Type = VarType2Type<VT>::type;
static constexpr size_t SizeOf(VarType vt)
{
switch(vt)
{
case(VarType::UNDEF): return 0;
case(VarType::FLOAT): return sizeof(Type<VarType::FLOAT>);
case(VarType::DOUBLE): return sizeof(Type<VarType::DOUBLE>);
case(VarType::INT1): return sizeof(Type<VarType::INT1>);
case(VarType::INT2): return sizeof(Type<VarType::INT2>);
case(VarType::INT4): return sizeof(Type<VarType::INT4>);
case(VarType::INT8): return sizeof(Type<VarType::INT8>);
case(VarType::UINT1): return sizeof(Type<VarType::UINT1>);
}
return 0;
}
template<class T> static size_t FindInd(const MString& name, const std::vector<T>& arr)
{
for(size_t i = 0; i < arr.size(); i++)
if(arr[i].Name() == name) return i;
return arr.size();
}
class Attribute: public AttVT
{
MString name;
public:
Attribute(const MString& n, AttVT&& v): AttVT(std::move(v)), name(n) {}
Attribute(const std::string& n, AttVT&& v): AttVT(std::move(v)), name(n.c_str(), n.size()) {}
const MString& Name() const { return name; }
AttType Type() const
{
if(std::holds_alternative<int8>(*this))
return AttType::INT;
else if(std::holds_alternative<uint8>(*this))
return AttType::UINT;
else if(std::holds_alternative<double>(*this))
return AttType::REAL;
else if(std::holds_alternative<MString>(*this))
return AttType::STRING;
else if(std::holds_alternative<bool>(*this))
return AttType::BOOL;
return AttType::UNDEF;
}
int8 I() const
{
if(std::holds_alternative<int8>(*this))
return std::get<int8>(*this);
else if(std::holds_alternative<uint8>(*this))
return int_cast<int8>(std::get<uint8>(*this));
else if(std::holds_alternative<double>(*this))
return static_cast<int8>(std::get<double>(*this));
else if(std::holds_alternative<MString>(*this))
return std::get<MString>(*this).ToInteger<int8>();
else if(std::holds_alternative<bool>(*this))
return std::get<bool>(*this) ? 1 : 0;
return 0;
}
uint8 U() const
{
if(std::holds_alternative<int8>(*this))
return int_cast<uint8>(std::get<int8>(*this));
else if(std::holds_alternative<uint8>(*this))
return std::get<uint8>(*this);
else if(std::holds_alternative<double>(*this))
return static_cast<uint8>(std::get<double>(*this));
else if(std::holds_alternative<MString>(*this))
return std::get<MString>(*this).ToInteger<uint8>();
else if(std::holds_alternative<bool>(*this))
return std::get<bool>(*this) ? 1 : 0;
return 0;
}
double D() const
{
if(std::holds_alternative<int8>(*this))
return std::get<int8>(*this);
else if(std::holds_alternative<uint8>(*this))
return std::get<uint8>(*this);
else if(std::holds_alternative<double>(*this))
return std::get<double>(*this);
else if(std::holds_alternative<MString>(*this))
return michlib_internal::RealType<sizeof(double)>::String2Real(std::get<MString>(*this).Buf());
else if(std::holds_alternative<bool>(*this))
return std::get<bool>(*this) ? 1 : 0;
return 0;
}
MString S() const
{
if(std::holds_alternative<int8>(*this))
return MString().FromInt(std::get<int8>(*this));
else if(std::holds_alternative<uint8>(*this))
return MString().FromUInt(std::get<uint8>(*this));
else if(std::holds_alternative<double>(*this))
return MString().FromReal(std::get<double>(*this));
else if(std::holds_alternative<MString>(*this))
return std::get<MString>(*this);
else if(std::holds_alternative<bool>(*this))
return MString().FromBool(std::get<bool>(*this));
return "";
}
bool B() const
{
if(std::holds_alternative<int8>(*this))
return std::get<int8>(*this) != 0;
else if(std::holds_alternative<uint8>(*this))
return std::get<uint8>(*this) != 0;
else if(std::holds_alternative<double>(*this))
return std::get<double>(*this) != 0.0;
else if(std::holds_alternative<MString>(*this))
return std::get<MString>(*this).ToBool();
else if(std::holds_alternative<bool>(*this))
return std::get<bool>(*this);
return false;
}
};
class Dimension
{
MString name;
size_t size;
public:
Dimension(const MString& str, size_t num): name(str), size(num) {}
const MString& Name() const { return name; }
size_t Size() const { return size; }
};
class Variable
{
public:
using FillType = std::variant<std::monostate, int8, uint8, double>;
private:
MString name;
VarType type = VarType::UNDEF;
std::vector<size_t> dims;
std::vector<Attribute> atts;
FillType fill;
public:
Variable(const MString& name_, VarType type_, std::vector<size_t>&& dims_, std::vector<Attribute>&& atts_, FillType fill_ = 0):
name(name_), type(type_), dims(std::move(dims_)), atts(std::move(atts_)), fill(fill_)
{
}
explicit operator bool() const { return type != VarType::UNDEF; }
const auto& Dims() const { return dims; }
size_t NDim() const { return dims.size(); }
size_t NAtt() const { return atts.size(); }
auto AttNames() const
{
std::vector<MString> out;
std::transform(atts.cbegin(), atts.cend(), std::back_inserter(out), [](const Attribute& a) { return a.Name(); });
return out;
}
AttType AttT(const MString& name) const
{
size_t ind = FindInd(name, atts);
return ind < atts.size() ? atts[ind].Type() : AttType::UNDEF;
}
int8 AttInt(const MString& name) const
{
size_t ind = FindInd(name, atts);
return ind < atts.size() ? atts[ind].I() : 0;
}
uint8 AttUInt(const MString& name) const
{
size_t ind = FindInd(name, atts);
return ind < atts.size() ? atts[ind].U() : 0;
}
double AttReal(const MString& name) const
{
size_t ind = FindInd(name, atts);
return ind < atts.size() ? atts[ind].D() : 0.0;
}
MString AttString(const MString& name) const
{
size_t ind = FindInd(name, atts);
return ind < atts.size() ? atts[ind].S() : MString();
}
bool AttBool(const MString& name) const
{
size_t ind = FindInd(name, atts);
return ind < atts.size() ? atts[ind].B() : false;
}
const MString& Name() const { return name; }
auto Type() const { return type; }
const auto& Fill() const { return fill; }
};
protected:
std::vector<Attribute> gats;
std::vector<Dimension> dims;
std::vector<Variable> vars;
public:
operator bool() const { return !vars.empty(); }
size_t NDim() const { return dims.size(); }
size_t NDim(const MString& var) const
{
size_t ind = FindInd(var, vars);
return ind < vars.size() ? vars[ind].NDim() : 0;
}
size_t NAtt() const { return gats.size(); }
auto AttNames() const
{
std::vector<MString> out;
std::transform(gats.cbegin(), gats.cend(), std::back_inserter(out), [](const Attribute& a) { return a.Name(); });
return out;
}
size_t NAtt(const MString& var) const
{
if(!var.Exist()) return NAtt();
size_t ind = FindInd(var, vars);
return ind < vars.size() ? vars[ind].NAtt() : 0;
}
auto AttNames(const MString& var) const
{
if(!var.Exist()) return AttNames();
size_t ind = FindInd(var, vars);
return ind < vars.size() ? vars[ind].AttNames() : decltype(AttNames())();
}
auto VarNames() const
{
std::vector<MString> out;
std::transform(vars.cbegin(), vars.cend(), std::back_inserter(out), [](const Variable& v) { return v.Name(); });
return out;
}
VarType VarT(const MString& var) const
{
size_t ind = FindInd(var, vars);
return ind < vars.size() ? vars[ind].Type() : VarType::UNDEF;
}
auto VarFill(const MString& var) const
{
size_t ind = FindInd(var, vars);
return ind < vars.size() ? vars[ind].Fill() : Variable::FillType();
}
auto DimNames() const
{
std::vector<MString> out;
std::transform(dims.cbegin(), dims.cend(), std::back_inserter(out), [](const Dimension& d) { return d.Name(); });
return out;
}
auto DimNames(const MString& var) const
{
size_t ind = FindInd(var, vars);
std::vector<MString> out;
if(ind >= vars.size()) return out;
auto vdims = vars[ind].Dims();
std::transform(vdims.cbegin(), vdims.cend(), std::back_inserter(out), [&dims = std::as_const(dims)](const size_t& i) { return dims[i].Name(); });
return out;
}
size_t DimSize(const MString& dim) const
{
size_t ind = FindInd(dim, dims);
return ind < dims.size() ? dims[ind].Size() : 0;
}
AttType AttT(const MString& var, const MString& name) const
{
if(!var.Exist())
{
size_t ind = FindInd(name, gats);
return ind < gats.size() ? gats[ind].Type() : AttType::UNDEF;
}
size_t ind = FindInd(var, vars);
return ind < vars.size() ? vars[ind].AttT(name) : AttType::UNDEF;
}
int8 AttInt(const MString& var, const MString& name) const
{
if(!var.Exist())
{
size_t ind = FindInd(name, gats);
return ind < gats.size() ? gats[ind].I() : 0;
}
size_t ind = FindInd(var, vars);
return ind < vars.size() ? vars[ind].AttInt(name) : 0;
}
uint8 AttUInt(const MString& var, const MString& name) const
{
if(!var.Exist())
{
size_t ind = FindInd(name, gats);
return ind < gats.size() ? gats[ind].U() : 0;
}
size_t ind = FindInd(var, vars);
return ind < vars.size() ? vars[ind].AttUInt(name) : 0;
}
double AttReal(const MString& var, const MString& name) const
{
if(!var.Exist())
{
size_t ind = FindInd(name, gats);
return ind < gats.size() ? gats[ind].D() : 0.0;
}
size_t ind = FindInd(var, vars);
return ind < vars.size() ? vars[ind].AttReal(name) : 0.0;
}
MString AttString(const MString& var, const MString& name) const
{
if(!var.Exist())
{
size_t ind = FindInd(name, gats);
return ind < gats.size() ? gats[ind].S() : MString();
}
size_t ind = FindInd(var, vars);
return ind < vars.size() ? vars[ind].AttString(name) : MString();
}
bool AttBool(const MString& var, const MString& name) const
{
if(!var.Exist())
{
size_t ind = FindInd(name, gats);
return ind < gats.size() ? gats[ind].B() : false;
}
size_t ind = FindInd(var, vars);
return ind < vars.size() ? vars[ind].AttBool(name) : false;
}
auto AttT(const MString& name) const { return AttT("", name); }
auto AttInt(const MString& name) const { return AttInt("", name); }
auto AttUInt(const MString& name) const { return AttUInt("", name); }
auto AttReal(const MString& name) const { return AttReal("", name); }
auto AttString(const MString& name) const { return AttString("", name); }
auto AttBool(const MString& name) const { return AttBool("", name); }
bool HasDim(const MString& name) const { return FindInd(name, dims) < dims.size(); }
bool HasVar(const MString& name) const { return FindInd(name, vars) < vars.size(); }
bool HasAtt(const MString& vname, const MString& aname) const { return AttT(vname, aname) != AttType::UNDEF; }
bool HasAtt(const MString& aname) const { return AttT(aname) != AttType::UNDEF; }
};
class DimReqDef
{
protected:
struct DimReq
{
static const auto fill = std::numeric_limits<size_t>::max();
MString name;
size_t beg, count;
DimReq(): name(MString()), beg(fill), count(fill) {}
DimReq(const char* n): name(n), beg(fill), count(fill) {}
DimReq(const MString& n): name(n), beg(fill), count(fill) {}
DimReq(MString&& n): name(std::move(n)), beg(fill), count(fill) {}
DimReq(const char* n, size_t s): name(n), beg(s), count(fill) {}
DimReq(const MString& n, size_t s): name(n), beg(s), count(fill) {}
DimReq(MString&& n, size_t s): name(std::move(n)), beg(s), count(fill) {}
DimReq(const char* n, size_t s, size_t c): name(n), beg(s), count(c) {}
DimReq(const MString& n, size_t s, size_t c): name(n), beg(s), count(c) {}
DimReq(MString&& n, size_t s, size_t c): name(std::move(n)), beg(s), count(c) {}
const MString& Name() const { return name; }
};
};
template<class C> class NcZarrRead: public C, public DimReqDef
{
template<class Data> static constexpr size_t Dimensionity()
{
if constexpr(requires(Data& d) { d(0, 0, 0, 0); }) return 4;
if constexpr(requires(Data& d) { d(0, 0, 0); }) return 3;
if constexpr(requires(Data& d) { d(0, 0); }) return 2;
if constexpr(requires(Data& d) { d(0); }) return 1;
return 0;
}
template<class Data, size_t D, class Dummy = void> struct DataTypeExtractorS;
template<class Data, class Dummy> struct DataTypeExtractorS<Data, 1, Dummy>
{
using type = std::decay_t<decltype(std::declval<Data>()(0))>;
};
template<class Data, class Dummy> struct DataTypeExtractorS<Data, 2, Dummy>
{
using type = std::decay_t<decltype(std::declval<Data>()(0, 0))>;
};
template<class Data, class Dummy> struct DataTypeExtractorS<Data, 3, Dummy>
{
using type = std::decay_t<decltype(std::declval<Data>()(0, 0, 0))>;
};
template<class Data, class Dummy> struct DataTypeExtractorS<Data, 4, Dummy>
{
using type = std::decay_t<decltype(std::declval<Data>()(0, 0, 0, 0))>;
};
template<class Data> using DataTypeExtractor = DataTypeExtractorS<Data, Dimensionity<Data>()>::type;
template<class VType, class Data, class Transform>
Error Read(const MString& vname, const std::vector<size_t>& transindex, Data& data, Transform transform, std::vector<DimReq> reqs) const
{
size_t nval = 1;
for(const auto& r: reqs) nval *= r.count;
const size_t indim = reqs.size();
constexpr size_t outdim = Dimensionity<Data>();
std::vector<size_t> start;
std::vector<size_t> count;
start.resize(indim);
count.resize(indim);
for(size_t i = 0; i < indim; i++)
{
start[i] = reqs[i].beg;
count[i] = reqs[i].count;
}
using DataType = DataTypeExtractor<Data>;
DataType fillout;
bool havefill = C::VarFill(vname).index() > 0;
VType fillin = std::visit(
[](auto v)
{
if constexpr(std::is_convertible_v<decltype(v), VType>)
return static_cast<VType>(v);
else
return std::numeric_limits<VType>::max();
},
C::VarFill(vname));
if constexpr(requires(Data& d) { // Data have own fillvalue
{
d.Fillval()
} -> std::convertible_to<DataType>;
})
fillout = data.Fillval();
else // Data does'nt have own fillvalue, using variable fillvalue
fillout = static_cast<DataType>(fillin);
auto ret = C::template Read<VType>(vname, start.data(), count.data());
if(!ret) return ret;
const auto& rawdata = ret.Value();
std::vector<size_t> mul(indim, 1);
for(size_t i = indim - 1; i > 0; i--) mul[i - 1] = mul[i] * count[i];
size_t inind = 0;
for(typename C::ArrCounter i(count); i; ++i)
{
// TODO: Remove this testing block
size_t cind = 0;
for(size_t j = 0; j < indim; j++) cind += i[j] * mul[j];
if(cind != inind) return {"NcZarrRead::Read", "Internal error"};
if(i.Index() != inind) return {"NcZarrRead::Read", "Internal error"};
if(inind != i.Index(i.Index(inind, count), count)) return {"NcZarrRead::Read", "Internal error"};
DataType out;
const VType& in = rawdata(inind);
if(havefill && in == fillin)
out = fillout;
else
out = transform(in);
if constexpr(outdim == 1)
data(i[transindex[0]]) = out;
else if constexpr(outdim == 2)
data(i[transindex[0]], i[transindex[1]]) = out;
else if constexpr(outdim == 3)
data(i[transindex[0]], i[transindex[1]], i[transindex[2]]) = out;
else if constexpr(outdim == 4)
data(i[transindex[0]], i[transindex[1]], i[transindex[2]], i[transindex[3]]) = out;
inind++;
}
michlib::message("Variable " + vname + ", request size " + nval);
for(const auto& r: reqs) michlib::message(r.name + " from " + r.beg + ", count " + r.count);
return Error();
}
public:
// Request is string
template<class Data, class Transform> Error Read(const MString& vname, Data& data, Transform transform, const char* request) const
{
return Read(vname, data, transform, MString(request));
}
// Request by one dimension
template<class Data, class Transform> Error Read(const MString& vname, Data& data, Transform transform, DimReq&& req1) const
{
return Read(vname, data, transform, std::vector<DimReq>{std::move(req1)});
}
// Request by two dimension
template<class Data, class Transform> Error Read(const MString& vname, Data& data, Transform transform, DimReq&& req1, DimReq&& req2) const
{
return Read(vname, data, transform, std::vector<DimReq>{std::move(req1), std::move(req2)});
}
// Request by three dimension
template<class Data, class Transform> Error Read(const MString& vname, Data& data, Transform transform, DimReq&& req1, DimReq&& req2, DimReq&& req3) const
{
return Read(vname, data, transform, std::vector<DimReq>{std::move(req1), std::move(req2), std::move(req3)});
}
// Request by four dimension
template<class Data, class Transform> Error Read(const MString& vname, Data& data, Transform transform, DimReq&& req1, DimReq&& req2, DimReq&& req3, DimReq&& req4) const
{
return Read(vname, data, transform, std::vector<DimReq>{std::move(req1), std::move(req2), std::move(req3), std::move(req4)});
}
// Request full variable
template<class Data, class Transform> Error Read(const MString& vname, Data& data, Transform transform) const
{
static const MString pref = "NcZarrRead::Read";
if(!C::HasVar(vname)) return {pref, "Variable " + vname + " not found"};
std::vector<struct DimReq> pdims;
const auto vdims = C::DimNames(vname);
std::transform(
vdims.cbegin(), vdims.cend(), std::back_inserter(pdims), [this](const MString& n) -> struct DimReq {
return {n, 0, C::DimSize(n)};
});
return Read(vname, data, transform, pdims);
}
// Base function for all Read's
template<class Data, class Transform> Error Read(const MString& vname, Data& data, Transform transform, std::vector<DimReq> reqs) const
{
static const MString pref = "NcZarrRead::Read";
if(!C::HasVar(vname)) return {pref, "Variable " + vname + " not found"};
std::vector<struct DimReq> pdims;
{
const auto vdims = C::DimNames(vname);
std::transform(
vdims.cbegin(), vdims.cend(), std::back_inserter(pdims), [](const MString& n) -> struct DimReq {
return {n, 0, 1};
});
}
std::vector<size_t> transindex;
// Parse request
if(reqs.size() == 0) return {pref, "Empty request"};
for(const auto& req: reqs)
{
size_t ind = C::FindInd(req.name, pdims);
if(ind >= pdims.size()) return {pref, "Variable " + vname + " has no dimension " + req.name};
for(size_t i = 0; i < transindex.size(); i++)
if(transindex[i] == ind) return {pref, "Parameters for dimension " + req.name + " already defined"};
transindex.push_back(ind);
size_t dlen = C::DimSize(pdims[ind].name);
if(req.beg == req.fill && req.count == req.fill) // Only name, so, we request full length
{
pdims[ind].beg = 0;
pdims[ind].count = dlen;
}
else if(req.count == req.fill) // Name and first index
{
pdims[ind].beg = req.beg;
pdims[ind].count = 1;
}
else // Name, first index, count
{
pdims[ind].beg = req.beg;
pdims[ind].count = req.count;
}
// Sanity checks
if(pdims[ind].count <= 0) return {pref, "Error parsing request: count must be greter then zero"};
if(pdims[ind].beg >= dlen) return {pref, MString("Error parsing request: start index ") + pdims[ind].beg + " must be lesser then " + pdims[ind].name + " size " + dlen};
if(pdims[ind].beg + pdims[ind].count > dlen)
return {pref, MString("Error parsing request: start index ") + pdims[ind].beg + " with count " + pdims[ind].count + " exceeds " + pdims[ind].name + " size " + dlen};
}
if(transindex.size() != Dimensionity<Data>()) return {pref, "Output data dimensions not correspondind request dimensions"};
switch(C::VarT(vname))
{
case(C::VarType::UNDEF): return {pref, "No variable with name " + vname + " (impossible)"};
case(C::VarType::FLOAT): return Read<typename C::template Type<C::VarType::FLOAT>>(vname, transindex, data, transform, pdims);
case(C::VarType::DOUBLE): return Read<typename C::template Type<C::VarType::DOUBLE>>(vname, transindex, data, transform, pdims);
case(C::VarType::INT1): return Read<typename C::template Type<C::VarType::INT1>>(vname, transindex, data, transform, pdims);
case(C::VarType::INT2): return Read<typename C::template Type<C::VarType::INT2>>(vname, transindex, data, transform, pdims);
case(C::VarType::INT4): return Read<typename C::template Type<C::VarType::INT4>>(vname, transindex, data, transform, pdims);
case(C::VarType::INT8): return Read<typename C::template Type<C::VarType::INT8>>(vname, transindex, data, transform, pdims);
case(C::VarType::UINT1): return Read<typename C::template Type<C::VarType::UINT1>>(vname, transindex, data, transform, pdims);
}
return {pref, "Internal error (impossible)"};
}
// Request by string argument
template<class Data, class Transform> Error Read(const MString& vname, Data& data, Transform transform, const MString& request) const
{
static const MString pref = "NcZarrRead::Read";
std::vector<struct DimReq> pdims;
// Parse request
const auto dimdesc = request.Split(";, \t");
if(dimdesc.size() == 0) return {pref, "Empty request"};
for(const auto& dd: dimdesc)
{
const auto dimpar = dd.Split(":", true);
if(dimpar.size() == 1) // Only name, so, we request full length
pdims.emplace_back(dimpar[0]);
else if(dimpar.size() == 2) // Name and first index
pdims.emplace_back(dimpar[0], dimpar[1].ToInteger<size_t>());
else if(dimpar.size() == 3) // Name, first index, count
pdims.emplace_back(dimpar[0], dimpar[1].ToInteger<size_t>(), dimpar[2].ToInteger<size_t>());
else
return {pref, "Can't parse expression " + dd};
}
return Read(vname, data, transform, pdims);
}
};