Michael Uleysky
8 months ago
4 changed files with 1235 additions and 2 deletions
@ -0,0 +1,822 @@ |
|||||||
|
#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); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,174 @@ |
|||||||
|
#pragma once |
||||||
|
#include "GPL.h" |
||||||
|
#include "cache.h" |
||||||
|
#include "curlfuncs.h" |
||||||
|
#include "nczarrcommon.h" |
||||||
|
#include <json/json.h> |
||||||
|
#include <variant> |
||||||
|
|
||||||
|
class ZarrTypes: public NcZarrTypes |
||||||
|
{ |
||||||
|
protected: |
||||||
|
template<class VType> class ReadedData |
||||||
|
{ |
||||||
|
//public:
|
||||||
|
using Vec = std::vector<size_t>; |
||||||
|
|
||||||
|
private: |
||||||
|
Vec start, chunkstart; |
||||||
|
ArrCounter mainind, chunkind, inchunkind; |
||||||
|
std::vector<std::unique_ptr<VType[]>> data; |
||||||
|
|
||||||
|
public: |
||||||
|
ReadedData():mainind(Vec()),chunkind(Vec()),inchunkind(Vec()){} |
||||||
|
|
||||||
|
ReadedData(size_t N, const size_t* start, const size_t* count, const size_t* csize, std::vector<std::unique_ptr<VType[]>>&& d): |
||||||
|
start(start, start + N), |
||||||
|
chunkstart( |
||||||
|
[](size_t N, const size_t* st, const size_t* cs) |
||||||
|
{ |
||||||
|
Vec out(N); |
||||||
|
for(size_t i = 0; i < N; i++) out[i] = st[i] / cs[i]; |
||||||
|
return out; |
||||||
|
}(N, start, csize)), |
||||||
|
mainind(Vec(count, count + N)), |
||||||
|
chunkind( |
||||||
|
[](size_t N, const size_t* st, const size_t* cn, const size_t* cs) |
||||||
|
{ |
||||||
|
Vec out(N); |
||||||
|
for(size_t i = 0; i < N; i++) out[i] = (st[i] + cn[i]) / cs[i] - st[i] / cs[i] + 1; |
||||||
|
return out; |
||||||
|
}(N, start, count, csize)), |
||||||
|
inchunkind(Vec(csize, csize + N)), |
||||||
|
data(std::move(d)) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
VType operator()(size_t lini) const |
||||||
|
{ |
||||||
|
Vec ind = mainind.Index(lini, mainind.Count()); |
||||||
|
Vec cind(ind.size()), inind(ind.size()); |
||||||
|
|
||||||
|
for(size_t i = 0; i < ind.size(); i++) |
||||||
|
{ |
||||||
|
cind[i] = (ind[i] + start[i]) / inchunkind.Count(i) - chunkstart[i]; // indes of chunk
|
||||||
|
inind[i] = (ind[i] + start[i]) % inchunkind.Count(i); // index inside chunk
|
||||||
|
} |
||||||
|
size_t chunk = chunkind.Index(cind); |
||||||
|
size_t inside = inchunkind.Index(inind); |
||||||
|
return data[chunk][inside]; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
private: |
||||||
|
// Create attribute from json value
|
||||||
|
static AttVT CreateAtt(const Json::Value& val) |
||||||
|
{ |
||||||
|
if(val.type() == Json::intValue) return AttVT{std::in_place_type<int8>, val.asInt64()}; |
||||||
|
if(val.type() == Json::uintValue) return AttVT(std::in_place_type<uint8>, val.asUInt64()); |
||||||
|
if(val.type() == Json::realValue) return AttVT(std::in_place_type<double>, val.asDouble()); |
||||||
|
if(val.type() == Json::stringValue) |
||||||
|
{ |
||||||
|
auto str = val.asString(); |
||||||
|
return AttVT(std::in_place_type<MString>, MString(str.c_str(), str.size())); |
||||||
|
} |
||||||
|
if(val.type() == Json::booleanValue) return AttVT(std::in_place_type<bool>, val.asBool()); |
||||||
|
return AttVT(); |
||||||
|
} |
||||||
|
|
||||||
|
public: |
||||||
|
// Read attributes from .zattrs
|
||||||
|
static auto ReadAtts(const Json::Value& obj) |
||||||
|
{ |
||||||
|
std::vector<Attribute> out; |
||||||
|
if(obj.type() != Json::objectValue) return out; |
||||||
|
const auto keys = obj.getMemberNames(); |
||||||
|
for(const auto& key: keys) |
||||||
|
if(key != "_ARRAY_DIMENSIONS") out.emplace_back(key, CreateAtt(obj[key])); |
||||||
|
return out; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
class ZarrFunctions: public ZarrTypes |
||||||
|
{ |
||||||
|
std::unique_ptr<GenericCache> cache; |
||||||
|
CURLRAII chandle; |
||||||
|
MString url; |
||||||
|
|
||||||
|
std::vector<std::vector<size_t>> chunks; |
||||||
|
|
||||||
|
// Find variable names in metadata
|
||||||
|
static std::vector<MString> ReadVarNames(const Json::Value& meta); |
||||||
|
|
||||||
|
Error AddVar(const MString& name, const Json::Value& zattrs, const Json::Value& zarray); |
||||||
|
|
||||||
|
protected: |
||||||
|
ZarrFunctions() |
||||||
|
{ |
||||||
|
auto oldprefix = michlib::GPL.UsePrefix("ZARR"); |
||||||
|
cache.reset(CreateCache(michlib::GPL.ParameterSValue("Cache", ""))); |
||||||
|
michlib::GPL.UsePrefix(oldprefix); |
||||||
|
if(!cache) |
||||||
|
{ |
||||||
|
michlib::errmessage("Can't init data cache"); |
||||||
|
cache.reset(new FakeCache); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
template<class VType> RetVal<ReadedData<VType>> Read(const MString& var, const size_t* start, const size_t* count) const |
||||||
|
{ |
||||||
|
using Vec = std::vector<size_t>; |
||||||
|
|
||||||
|
size_t ind = FindInd(var, vars); |
||||||
|
const size_t N = vars[ind].NDim(); |
||||||
|
const auto& csize = chunks[ind]; |
||||||
|
Vec chunkstart( |
||||||
|
[](size_t N, const size_t* st, const size_t* cs) |
||||||
|
{ |
||||||
|
Vec out(N); |
||||||
|
for(size_t i = 0; i < N; i++) out[i] = st[i] / cs[i]; |
||||||
|
return out; |
||||||
|
}(N, start, csize.data())); |
||||||
|
ArrCounter chunkind( |
||||||
|
[](size_t N, const size_t* st, const size_t* cn, const size_t* cs) |
||||||
|
{ |
||||||
|
Vec out(N); |
||||||
|
for(size_t i = 0; i < N; i++) out[i] = (st[i] + cn[i]) / cs[i] - st[i] / cs[i] + 1; |
||||||
|
return out; |
||||||
|
}(N, start, count, csize.data())); |
||||||
|
|
||||||
|
bool havefill = vars[ind].Fill().index() > 0; |
||||||
|
VType fill = 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(); |
||||||
|
}, |
||||||
|
vars[ind].Fill()); |
||||||
|
|
||||||
|
std::vector<std::unique_ptr<VType[]>> cdata; |
||||||
|
|
||||||
|
size_t chunksize = 1; |
||||||
|
for(const auto c: csize) chunksize *= c; |
||||||
|
|
||||||
|
cdata.resize(chunkind.N()); |
||||||
|
|
||||||
|
for(; chunkind; ++chunkind) |
||||||
|
{ |
||||||
|
cdata[chunkind.Index()].reset(new VType[chunksize]); |
||||||
|
auto res = GetChunk(var, chunkind.VIndex(chunkstart), chunksize, sizeof(VType), cdata[chunkind.Index()].get(),havefill?&fill:nullptr); |
||||||
|
if(!res) return res; |
||||||
|
} |
||||||
|
|
||||||
|
return ReadedData<VType>(N, start, count, csize.data(), std::move(cdata)); |
||||||
|
} |
||||||
|
|
||||||
|
Error GetChunk(const MString& var, const std::vector<size_t>& chunkind, size_t chunksize, size_t elsize, void* data, const void* fill) const; |
||||||
|
|
||||||
|
public: |
||||||
|
Error Open(const MString& product, const MString& dataset, bool time = true); |
||||||
|
}; |
||||||
|
|
||||||
|
using Zarr = NcZarrRead<ZarrFunctions>; |
@ -0,0 +1,236 @@ |
|||||||
|
#define MICHLIB_NOSOURCE |
||||||
|
#include "zarr.h" |
||||||
|
#include "copcat.h" |
||||||
|
#include <blosc.h> |
||||||
|
|
||||||
|
std::vector<MString> ZarrFunctions::ReadVarNames(const Json::Value& meta) |
||||||
|
{ |
||||||
|
std::vector<MString> out; |
||||||
|
if(meta.type() != Json::objectValue) return out; |
||||||
|
const auto keys = meta.getMemberNames(); |
||||||
|
for(const auto& key: keys) |
||||||
|
{ |
||||||
|
if(!key.ends_with("/.zarray")) continue; |
||||||
|
const auto vname = key.substr(0, key.size() - 8); |
||||||
|
const auto& zattr = meta[vname + "/.zattrs"]; |
||||||
|
if(!(zattr && zattr.type() == Json::objectValue)) continue; |
||||||
|
|
||||||
|
MString name(vname.c_str(), vname.size()); |
||||||
|
bool found = false; |
||||||
|
for(size_t id = 0; id < out.size(); id++) |
||||||
|
if(out[id] == name) |
||||||
|
{ |
||||||
|
found = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
if(!found) out.emplace_back(std::move(name)); |
||||||
|
} |
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
Error ZarrFunctions::AddVar(const MString& name, const Json::Value& zattrs, const Json::Value& zarray) |
||||||
|
{ |
||||||
|
static const MString pref = "Zarr::AddVar"; |
||||||
|
|
||||||
|
VarType newtype; |
||||||
|
|
||||||
|
Variable::FillType fill; |
||||||
|
|
||||||
|
// Checks for parameters in zarray
|
||||||
|
{ |
||||||
|
const auto& cid = zarray["compressor"]["id"]; |
||||||
|
if(!cid || cid.type() != Json::stringValue || cid.asString() != "blosc") return {pref, "Unsupported compressor: " + MString(cid.asString().c_str())}; |
||||||
|
} |
||||||
|
{ |
||||||
|
const auto& zf = zarray["zarr_format"]; |
||||||
|
if(!zf || (zf.type() != Json::uintValue && zf.type() != Json::intValue) || zf.asUInt() != 2) return {pref, "Unsupported format version: " + MString(zf.asUInt())}; |
||||||
|
} |
||||||
|
{ |
||||||
|
const auto& ord = zarray["order"]; |
||||||
|
if(!ord || ord.type() != Json::stringValue || ord.asString() != "C") return {pref, "Order in not C"}; |
||||||
|
} |
||||||
|
{ |
||||||
|
const auto& f = zarray["filters"]; |
||||||
|
if(f.type() != Json::nullValue) return {pref, "Filters is not null"}; |
||||||
|
} |
||||||
|
|
||||||
|
// Read dtype
|
||||||
|
{ |
||||||
|
const auto& dtype = zarray["dtype"]; |
||||||
|
if(!dtype || dtype.type() != Json::stringValue) return {pref, "No datatype"}; |
||||||
|
const auto str = dtype.asString(); |
||||||
|
if(str == "<f4") |
||||||
|
newtype = VarType::FLOAT; |
||||||
|
else if(str == "<f8") |
||||||
|
newtype = VarType::DOUBLE; |
||||||
|
else if(str == "|i1") |
||||||
|
newtype = VarType::INT1; |
||||||
|
else if(str == "|u1") |
||||||
|
newtype = VarType::UINT1; |
||||||
|
else if(str == "<i2") |
||||||
|
newtype = VarType::INT2; |
||||||
|
else if(str == "<i4") |
||||||
|
newtype = VarType::INT4; |
||||||
|
else if(str == "<i8") |
||||||
|
newtype = VarType::INT8; |
||||||
|
else |
||||||
|
return {pref, "Unsupported datatype: " + MString(str.c_str())}; |
||||||
|
} |
||||||
|
|
||||||
|
// Read fill_value
|
||||||
|
{ |
||||||
|
const auto& fillval = zarray["fill_value"]; |
||||||
|
if(!fillval) return {pref, "No fillval"}; |
||||||
|
if(fillval.type() == Json::uintValue) |
||||||
|
fill = fillval.asUInt64(); |
||||||
|
else if(fillval.type() == Json::intValue) |
||||||
|
fill = fillval.asInt64(); |
||||||
|
else if(fillval.type() == Json::realValue) |
||||||
|
fill = fillval.asDouble(); |
||||||
|
else if(fillval.type() == Json::stringValue && fillval.asString() == "NaN") |
||||||
|
fill = NAN; |
||||||
|
} |
||||||
|
|
||||||
|
// Read attributes
|
||||||
|
auto atts = ReadAtts(zattrs); |
||||||
|
|
||||||
|
std::vector<MString> dnames; |
||||||
|
std::vector<size_t> dsizes; |
||||||
|
std::vector<size_t> csizes; |
||||||
|
std::vector<size_t> dids; |
||||||
|
|
||||||
|
// Read dimensions names
|
||||||
|
{ |
||||||
|
const auto& arrdim = zattrs["_ARRAY_DIMENSIONS"]; |
||||||
|
if(!(arrdim && arrdim.type() == Json::arrayValue)) return {pref, "_ARRAY_DIMENSIONS not found"}; |
||||||
|
for(Json::ArrayIndex i = 0; i < arrdim.size(); i++) |
||||||
|
if(const auto& dim = arrdim[i]; dim.type() == Json::stringValue) |
||||||
|
{ |
||||||
|
const auto val = dim.asString(); |
||||||
|
dnames.emplace_back(val.c_str(), val.size()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Read dimensions sizes
|
||||||
|
{ |
||||||
|
const auto& shape = zarray["shape"]; |
||||||
|
if(!(shape && shape.type() == Json::arrayValue)) return {pref, "shape not found"}; |
||||||
|
for(Json::ArrayIndex i = 0; i < shape.size(); i++) |
||||||
|
if(const auto& s = shape[i]; s.type() == Json::uintValue || s.type() == Json::intValue) dsizes.push_back(s.asUInt()); |
||||||
|
} |
||||||
|
|
||||||
|
// Read chunk sizes
|
||||||
|
{ |
||||||
|
const auto& chunk = zarray["chunks"]; |
||||||
|
if(!(chunk && chunk.type() == Json::arrayValue)) return {pref, "chunks not found"}; |
||||||
|
for(Json::ArrayIndex i = 0; i < chunk.size(); i++) |
||||||
|
if(const auto& c = chunk[i]; c.type() == Json::uintValue || c.type() == Json::intValue) csizes.push_back(c.asUInt()); |
||||||
|
} |
||||||
|
|
||||||
|
if(dnames.size() != dsizes.size() || dnames.size() != csizes.size()) return {pref, "shape and chunks are in contradiction"}; |
||||||
|
|
||||||
|
dids.resize(dnames.size()); |
||||||
|
|
||||||
|
// Check dimensions names and sizes
|
||||||
|
for(size_t i = 0; i < dnames.size(); i++) |
||||||
|
{ |
||||||
|
bool found = false; |
||||||
|
for(size_t id = 0; id < dims.size(); id++) |
||||||
|
if(dims[id].Name() == dnames[i]) |
||||||
|
{ |
||||||
|
found = true; |
||||||
|
if(dims[id].Size() != dsizes[i]) |
||||||
|
return {pref, "According to previous data, the dimension " + dnames[i] + " has a size of " + dims[id].Size() + ", but here it is defined as " + dsizes[i]}; |
||||||
|
dids[i] = id; |
||||||
|
break; |
||||||
|
} |
||||||
|
if(!found) |
||||||
|
{ |
||||||
|
dids[i] = dims.size(); |
||||||
|
dims.emplace_back(dnames[i], dsizes[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
vars.emplace_back(name, newtype, std::move(dids), std::move(atts), fill); |
||||||
|
chunks.push_back(std::move(csizes)); |
||||||
|
|
||||||
|
return Error(); |
||||||
|
} |
||||||
|
|
||||||
|
Error ZarrFunctions::GetChunk(const MString& var, const std::vector<size_t>& chunkind, size_t chunksize, size_t elsize, void* data, const void* fill) const |
||||||
|
{ |
||||||
|
static const MString pref = "Zarr::GetChunk"; |
||||||
|
|
||||||
|
MString str = url + "/" + var + "/"; |
||||||
|
for(size_t i = 0; i < chunkind.size(); i++) str += (i == 0 ? "" : ".") + MString(chunkind[i]); |
||||||
|
|
||||||
|
auto [content, suc] = cache->Get(str); |
||||||
|
|
||||||
|
if(!suc) |
||||||
|
{ |
||||||
|
michlib::message(str + " not found in cache, downloading"); |
||||||
|
auto [out, res] = GetUrl(chandle, str); |
||||||
|
if(res != CURLE_OK) return Error(pref, MString("can't download chunk: ") + chandle.Err()); |
||||||
|
long respcode; |
||||||
|
curl_easy_getinfo(chandle, CURLINFO_RESPONSE_CODE, &respcode); |
||||||
|
michlib::message("Response: ", respcode); |
||||||
|
if(respcode == 403) out = ""; // Failed chunk download mean that this chunk contains only fill
|
||||||
|
cache->Put(str, out, 3600); |
||||||
|
content = std::move(out); |
||||||
|
} |
||||||
|
|
||||||
|
if(content.Exist()) |
||||||
|
{ |
||||||
|
size_t nb, cb, bs; |
||||||
|
blosc_cbuffer_sizes(content.Buf(), &nb, &cb, &bs); |
||||||
|
if(cb != content.Len()) return Error(pref, MString("bytes download: ") + content.Len() + ", but compressed bytes " + cb); |
||||||
|
if(nb != chunksize * elsize) return Error(pref, MString("decompressed bytes: ") + nb + ", but buffer size " + chunksize * elsize); |
||||||
|
auto res = blosc_decompress_ctx(content.Buf(), data, chunksize * elsize, 1); |
||||||
|
if(int_cast<size_t>(res) != chunksize * elsize) return Error(pref, MString("decompress only ") + res + " bytes of " + chunksize * elsize); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if(fill == nullptr) return Error(pref, MString("can't download chunk: ") + chandle.Err()); |
||||||
|
for(size_t i = 0; i < chunksize; i++) memcpy(michlib::P1(data) + i * elsize, fill, elsize); |
||||||
|
} |
||||||
|
|
||||||
|
return Error(); |
||||||
|
} |
||||||
|
|
||||||
|
Error ZarrFunctions::Open(const MString& product, const MString& dataset, bool time) |
||||||
|
{ |
||||||
|
static const MString pref = "Zarr::Open"; |
||||||
|
|
||||||
|
gats.clear(); |
||||||
|
dims.clear(); |
||||||
|
vars.clear(); |
||||||
|
|
||||||
|
CopernicusCatalog cat; |
||||||
|
Json::Value json; |
||||||
|
|
||||||
|
{ |
||||||
|
auto urlret = time ? cat.DatasetTimeURL(product, dataset) : cat.DatasetGeoURL(product, dataset); |
||||||
|
if(!urlret) return urlret.Add(pref, "Can't get url for the dataset " + dataset + " of product " + product); |
||||||
|
url = urlret.Value(); |
||||||
|
|
||||||
|
auto ret = cat.GetJSON(url + "/.zmetadata"); |
||||||
|
if(ret) |
||||||
|
json = ret.Value(); |
||||||
|
else |
||||||
|
return ret.Add(pref, "can't download .zmetadata"); |
||||||
|
} |
||||||
|
|
||||||
|
const auto& meta = json["metadata"]; |
||||||
|
if(!meta) return {pref, "No \"metadata\" key in JSON data"}; |
||||||
|
|
||||||
|
if(meta[".zattrs"]) gats = ReadAtts(meta[".zattrs"]); |
||||||
|
auto vnames = ReadVarNames(meta); |
||||||
|
|
||||||
|
for(size_t i = 0; i < vnames.size(); i++) |
||||||
|
{ |
||||||
|
auto err = AddVar(vnames[i], meta[(vnames[i] + "/.zattrs").Buf()], meta[(vnames[i] + "/.zarray").Buf()]); |
||||||
|
if(!err) return err.Add(pref, "Can't init variable " + vnames[i]); |
||||||
|
} |
||||||
|
|
||||||
|
return Error(); |
||||||
|
} |
Loading…
Reference in new issue