Commit 0e841d2c authored by Morten Ofstad's avatar Morten Ofstad
Browse files

* Added code to serialize VolumeDataLayout objects

* Added test for serialization of VolumeDataLayout objects
* Added a 'keys' vector to the MetadataContainer and added a MetadataType field to the keys to allow for ordered iteration through the metadata key/value pairs.
* Changed the AWS settings for test data to be CMake options instead of environment variables to make it work the same as locating test data.
* Added a getLayoutDescriptor() method to VolumeDataLayout and added some member variables to hold missing information about overall LOD settings.
* Fixed Base64Encode() functions and allow empty input to Base64Decode() function.
parent 76320726
......@@ -2,6 +2,10 @@ cmake_minimum_required(VERSION 3.10.2)
project(Open-VDS)
set (TEST_DATA_PATH "" CACHE PATH "Test data path")
set (TEST_AWS_REGION "" CACHE STRING "Test AWS region")
set (TEST_AWS_BUCKET "" CACHE STRING "Test AWS bucket")
set (TEST_AWS_OBJECTID "" CACHE STRING "Test AWS object ID")
set (CMAKE_CXX_STANDARD 11)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
......
......@@ -54,17 +54,104 @@ VolumeDataLayout *layout(VDSHandle *handle)
return handle->volumeDataLayout.get();
}
const char *addDescriptorString(std::string const &descriptorString, VDSHandle &handle)
{
char *data = new char[descriptorString.size() + 1];
memcpy(data, descriptorString.data(), descriptorString.size());
data[descriptorString.size()] = 0;
handle.descriptorStrings.emplace_back(data);
return data;
}
static int32_t getInternalCubeSizeLOD0(const VolumeDataLayoutDescriptor &desc)
{
int32_t size = int32_t(1) << desc.getBrickSize();
size -= desc.getNegativeMargin();
size -= desc.getPositiveMargin();
assert(size > 0);
return size;
}
static int32_t getLODCount(const VolumeDataLayoutDescriptor &desc)
{
return desc.getLODLevels() + 1;
}
void createVolumeDataLayout(VDSHandle &handle)
{
//handle.volumeDataLayout.reset(new VolumeDataLayout(handle.channelDescriptors)
int32_t dimensionality = int32_t(handle.axisDescriptors.size());
// Check if input layouts are valid so we can create a new layout
if (dimensionality < 2)
{
handle.volumeDataLayout.reset();
return;
}
handle.volumeDataLayout.reset(
new VolumeDataLayout(
handle,
handle.layoutDescriptor,
handle.axisDescriptors,
handle.channelDescriptors,
0, //MIA for now
{ 1, 0 }, //MIA for now
VolumeDataHash::getUniqueHash(),
CompressionMethod::None,
0,
false,
0));
for(int32_t iDimensionGroup = 0; iDimensionGroup < DimensionGroup_3D_Max; iDimensionGroup++)
{
DimensionGroup dimensionGroup = (DimensionGroup)iDimensionGroup;
int32_t nChunkDimensionality = DimensionGroupUtil::getDimensionality(dimensionGroup);
// Check if highest dimension in chunk is higher than the highest dimension in the dataset or 1D
if(DimensionGroupUtil::getDimension(dimensionGroup, nChunkDimensionality - 1) >= dimensionality ||
nChunkDimensionality == 1)
{
continue;
}
assert(nChunkDimensionality == 2 || nChunkDimensionality == 3);
int32_t physicalLODLevels = (nChunkDimensionality == 3 || handle.layoutDescriptor.isCreate2DLODs()) ? getLODCount(handle.layoutDescriptor) : 1;
int32_t brickSize = getInternalCubeSizeLOD0(handle.layoutDescriptor) * (nChunkDimensionality == 2 ? handle.layoutDescriptor.getBrickSizeMultiplier2D() : 1);
handle.volumeDataLayout->createRenderLayers(dimensionGroup, brickSize, physicalLODLevels);
}
}
VDSHandle* create(const OpenOptions& options, VolumeDataLayoutDescriptor const &layoutDescriptor, std::vector<VolumeDataAxisDescriptor> const &axisDescriptors, std::vector<VolumeDataChannelDescriptor> const &channelDescriptors, MetadataContainer const &metadataContainer, Error &error)
{
error = Error();
std::unique_ptr<VDSHandle> ret(new VDSHandle(options, error));
std::unique_ptr<VDSHandle> handle(new VDSHandle(options, error));
for(auto axisDescriptor : axisDescriptors)
{
handle->axisDescriptors.push_back(VolumeDataAxisDescriptor(axisDescriptor.getNumSamples(), addDescriptorString(axisDescriptor.getName(), *handle), addDescriptorString(axisDescriptor.getUnit(), *handle), axisDescriptor.getCoordinateMin(), axisDescriptor.getCoordinateMax()));
}
for(auto channelDescriptor : channelDescriptors)
{
handle->channelDescriptors.push_back(VolumeDataChannelDescriptor(channelDescriptor.getFormat(), channelDescriptor.getComponents(), addDescriptorString(channelDescriptor.getName(), *handle), addDescriptorString(channelDescriptor.getUnit(), *handle), channelDescriptor.getValueRangeMin(), channelDescriptor.getValueRangeMax()));
}
createVolumeDataLayout(*handle);
if (error.code)
return nullptr;
if (!serializeAndUploadVDSJson(*ret.get(), error))
if (!serializeAndUploadVDSJson(*handle, error))
return nullptr;
return ret.release();
return handle.release();
}
void destroy(VDSHandle *handle)
......
......@@ -26,13 +26,33 @@
namespace OpenVDS
{
enum class MetadataType
{
Int,
IntVector2,
IntVector3,
IntVector4,
Float,
FloatVector2,
FloatVector3,
FloatVector4,
Double,
DoubleVector2,
DoubleVector3,
DoubleVector4,
String,
BLOB
};
struct MetadataKey
{
std::string category;
std::string name;
MetadataType type;
std::string category;
std::string name;
};
inline bool operator==(const MetadataKey& a, const MetadataKey& b) { return a.category == b.category && a.name == b.name; }
inline bool operator==(const MetadataKey& a, const MetadataKey& b) { return a.type == b.type && a.category == b.category && a.name == b.name; }
} // end namespace OpenVDS
......@@ -68,11 +88,12 @@ struct MetadataContainer
std::unordered_map<MetadataKey, DoubleVector2> doubleVector2Data;
std::unordered_map<MetadataKey, DoubleVector3> doubleVector3Data;
std::unordered_map<MetadataKey, DoubleVector4> doubleVector4Data;
std::unordered_map<MetadataKey, std::string> stringData;
std::unordered_map<MetadataKey, std::vector<uint8_t>> blobData;
std::vector<MetadataKey> keys;
};
} // end namespace OpenVDS
......
......@@ -79,6 +79,9 @@ struct VDSHandle
layerMetadataContainer;
};
const char *addDescriptorString(std::string const &descriptorString, VDSHandle &handle);
void createVolumeDataLayout(VDSHandle &handle);
}
#endif //OPENVDSHANDLE_H
......@@ -62,7 +62,7 @@ bool Base64Decode(const char *data, int64_t len, std::vector<unsigned char> &res
// skip leading whitespace
while(len && isspace(*data)) len--, data++;
if(len == 0) { error = true; }
if(len == 0) { return true; }
result.reserve(result.size() + len / 4 * 3);
......@@ -92,21 +92,21 @@ bool Base64Decode(const char *data, int64_t len, std::vector<unsigned char> &res
void Base64Encode(const unsigned char *data, int64_t len, std::vector<char> &result)
{
result.reserve(((len + 2) / 3) * 4);
result.reserve(result.size() + ((len + 2) / 3) * 4);
while(len > 0)
{
int a = data[0] & 0x3f, b = (data[0] & 0xc0) >> 2, c = 0, d = 0;
int a = data[0] >> 2, b = (data[0] & 0x03) << 4, c = 0, d = 0;
if(len > 1) { b |= (data[1] & 0x0f); c = (data[1] & 0xf0) >> 2; }
if(len > 2) { c |= (data[2] & 0x03); d = (data[2] & 0xfc) >> 2; }
if(len > 1) { b |= data[1] >> 4; c = (data[1] & 0x0f) << 2; }
if(len > 2) { c |= data[2] >> 6; d = (data[2] & 0x3f) << 0; }
result.push_back(Base64Table::encode(a));
result.push_back(Base64Table::encode(b));
result.push_back((len > 1) ? Base64Table::encode(c) : '=');
result.push_back((len > 2) ? Base64Table::encode(d) : '=');
len -= 3;
len -= 3; data += 3;
}
}
}
......@@ -279,15 +279,6 @@ static enum VolumeDataLayoutDescriptor::LODLevels lodLevelsFromJson(Json::Value
throw Json::Exception("Illegal LOD levels");
}
const char *addDescriptorString(std::string const &descriptorString, VDSHandle &handle)
{
char *data = new char[descriptorString.size() + 1];
memcpy(data, descriptorString.data(), descriptorString.size());
data[descriptorString.size()] = 0;
handle.descriptorStrings.emplace_back(data);
return data;
}
static VolumeDataChannelDescriptor::Format voxelFormatFromJson(Json::Value const &jsonVoxelFormat)
{
std::string voxelFormatString = jsonVoxelFormat.asString();
......@@ -357,7 +348,71 @@ static VolumeDataMapping channelMappingFromJson(Json::Value const &jsonChannelMa
throw Json::Exception("Illegal channel mapping");
}
static bool parseJSONFromBuffer(const std::vector<uint8_t> &json, Json::Value &root, Error &error)
static MetadataType metadataTypeFromJson(Json::Value const &jsonMetadataType)
{
std::string metadataTypeString = jsonMetadataType.asString();
if(metadataTypeString == "Int")
{
return MetadataType::Int;
}
else if(metadataTypeString == "IntVector2")
{
return MetadataType::IntVector2;
}
else if(metadataTypeString == "IntVector3")
{
return MetadataType::IntVector3;
}
else if(metadataTypeString == "IntVector4")
{
return MetadataType::IntVector4;
}
else if(metadataTypeString == "Float")
{
return MetadataType::Float;
}
else if(metadataTypeString == "FloatVector2")
{
return MetadataType::FloatVector2;
}
else if(metadataTypeString == "FloatVector3")
{
return MetadataType::FloatVector3;
}
else if(metadataTypeString == "FloatVector4")
{
return MetadataType::FloatVector4;
}
else if(metadataTypeString == "Double")
{
return MetadataType::Double;
}
else if(metadataTypeString == "DoubleVector2")
{
return MetadataType::DoubleVector2;
}
else if(metadataTypeString == "DoubleVector3")
{
return MetadataType::DoubleVector3;
}
else if(metadataTypeString == "DoubleVector4")
{
return MetadataType::DoubleVector4;
}
else if(metadataTypeString == "String")
{
return MetadataType::String;
}
else if(metadataTypeString == "BLOB")
{
return MetadataType::BLOB;
}
throw Json::Exception("Illegal metadata type");
}
bool parseJSONFromBuffer(const std::vector<uint8_t> &json, Json::Value &root, Error &error)
{
try
{
......@@ -458,7 +513,9 @@ bool parseVolumeDataLayout(const std::vector<uint8_t> &json, VDSHandle &handle,
for (const Json::Value &metadata : root["metadata"])
{
MetadataKey key = { metadata["category"].asString(), metadata["name"].asString() };
MetadataKey key = { metadataTypeFromJson(metadata["type"]), metadata["category"].asString(), metadata["name"].asString() };
handle.metadataContainer.keys.push_back(key);
if (metadata["type"].asString() == "Int")
{
......@@ -706,69 +763,255 @@ bool parseLayerStatus(const std::vector<uint8_t> &json, VDSHandle &handle, Error
return true;
}
static int32_t getInternalCubeSizeLOD0(const VolumeDataLayoutDescriptor &desc)
Json::Value serializeAxisDescriptor(VolumeDataAxisDescriptor const &axisDescriptor)
{
int32_t size = int32_t(1) << desc.getBrickSize();
Json::Value axisDescriptorJson;
size -= desc.getNegativeMargin();
size -= desc.getPositiveMargin();
axisDescriptorJson["numSamples"] = axisDescriptor.getNumSamples();
axisDescriptorJson["name"] = axisDescriptor.getName();
axisDescriptorJson["unit"] = axisDescriptor.getUnit();
axisDescriptorJson["coordinateMin"] = axisDescriptor.getCoordinateMin();
axisDescriptorJson["coordinateMax"] = axisDescriptor.getCoordinateMax();
assert(size > 0);
return axisDescriptorJson;
}
std::string to_string(VolumeDataChannelDescriptor::Format format)
{
switch(format)
{
case VolumeDataChannelDescriptor::Format_1Bit: return "Format_1Bit";
case VolumeDataChannelDescriptor::Format_U8: return "Format_U8";
case VolumeDataChannelDescriptor::Format_U16: return "Format_U16";
case VolumeDataChannelDescriptor::Format_R32: return "Format_R32";
case VolumeDataChannelDescriptor::Format_U32: return "Format_U32";
case VolumeDataChannelDescriptor::Format_R64: return "Format_R64";
case VolumeDataChannelDescriptor::Format_U64: return "Format_U64";
default: assert(0 && "Illegal format"); return "";
};
}
std::string to_string(VolumeDataChannelDescriptor::Components components)
{
switch(components)
{
case VolumeDataChannelDescriptor::Components_1: return "Components_1";
case VolumeDataChannelDescriptor::Components_2: return "Components_2";
case VolumeDataChannelDescriptor::Components_4: return "Components_4";
default: assert(0 && "Illegal components"); return "";
};
}
std::string to_string(VolumeDataMapping mapping)
{
switch(mapping)
{
case VolumeDataMapping::Direct: return "Direct";
case VolumeDataMapping::PerTrace: return "PerTrace";
return size;
default: assert(0 && "Illegal mapping"); return "";
};
}
static int32_t getLODCount(const VolumeDataLayoutDescriptor &desc)
Json::Value serializeChannelDescriptor(VolumeDataChannelDescriptor const &channelDescriptor)
{
return desc.getLODLevels() + 1;
Json::Value valueRangeJson(Json::ValueType::arrayValue);
valueRangeJson.append(channelDescriptor.getValueRangeMin());
valueRangeJson.append(channelDescriptor.getValueRangeMax());
Json::Value channelDescriptorJson;
channelDescriptorJson["format"] = to_string(channelDescriptor.getFormat());
channelDescriptorJson["components"] = to_string(channelDescriptor.getComponents());
channelDescriptorJson["name"] = channelDescriptor.getName();
channelDescriptorJson["unit"] = channelDescriptor.getUnit();
channelDescriptorJson["valueRange"] = valueRangeJson;
channelDescriptorJson["channelMapping"] = to_string(channelDescriptor.getMapping());
channelDescriptorJson["mappedValues"] = channelDescriptor.getMappedValueCount();
channelDescriptorJson["discrete"] = channelDescriptor.isDiscrete();
channelDescriptorJson["renderable"] = channelDescriptor.isRenderable();
channelDescriptorJson["allowLossyCompression"] = channelDescriptor.isAllowLossyCompression();
channelDescriptorJson["useNoValue"] = channelDescriptor.isUseNoValue();
channelDescriptorJson["noValue"] = channelDescriptor.getNoValue();
channelDescriptorJson["integerScale"] = channelDescriptor.getIntegerScale();
channelDescriptorJson["integerOffset"] = channelDescriptor.getIntegerOffset();
return channelDescriptorJson;
}
static void createVolumeDataLayout(VDSHandle &handle)
template<typename T, int N>
Json::Value serializeVector(Vector<T, N> const &vector)
{
//handle.volumeDataLayout.reset(new VolumeDataLayout(handle.channelDescriptors)
int32_t dimensionality = int32_t(handle.axisDescriptors.size());
static_assert(N > 1 && N <= 4, "Only vectors with 2, 3 or 4 elements are supported");
// Check if input layouts are valid so we can create a new layout
if (dimensionality < 2)
Json::Value vectorJson(Json::ValueType::arrayValue);
int i = 0;
switch(N)
{
handle.volumeDataLayout.reset();
return;
case 4: vectorJson.append(vector[i++]);
case 3: vectorJson.append(vector[i++]);
case 2: vectorJson.append(vector[i++]);
vectorJson.append(vector[i++]);
}
handle.volumeDataLayout.reset(
new VolumeDataLayout(
handle,
handle.layoutDescriptor,
handle.axisDescriptors,
handle.channelDescriptors,
0, //MIA for now
{ 1, 0 }, //MIA for now
VolumeDataHash::getUniqueHash(),
CompressionMethod::None,
0,
false,
0));
return vectorJson;
}
Json::Value serializeBLOB(std::vector<uint8_t> const &blob)
{
std::vector<char> base64;
Base64Encode(blob.data(), blob.size(), base64);
return Json::Value(&base64[0], &base64[0] + base64.size());
}
Json::Value serializeMetadata(MetadataContainer const &metadataContainer)
{
Json::Value
metadataJsonArray(Json::ValueType::arrayValue);
for(int32_t iDimensionGroup = 0; iDimensionGroup < DimensionGroup_3D_Max; iDimensionGroup++)
for(auto metadataKey : metadataContainer.keys)
{
DimensionGroup dimensionGroup = (DimensionGroup)iDimensionGroup;
Json::Value metadataJson;
int32_t nChunkDimensionality = DimensionGroupUtil::getDimensionality(dimensionGroup);
metadataJson["category"] = metadataKey.category;
metadataJson["name"] = metadataKey.name;
// Check if highest dimension in chunk is higher than the highest dimension in the dataset or 1D
if(DimensionGroupUtil::getDimension(dimensionGroup, nChunkDimensionality - 1) >= dimensionality ||
nChunkDimensionality == 1)
switch(metadataKey.type)
{
continue;
case MetadataType::Int: metadataJson["type"] = "Int"; metadataJson["value"] = Json::Value(metadataContainer.intData.at(metadataKey)); break;
case MetadataType::IntVector2: metadataJson["type"] = "IntVector2"; metadataJson["value"] = serializeVector(metadataContainer.intVector2Data.at(metadataKey)); break;
case MetadataType::IntVector3: metadataJson["type"] = "IntVector3"; metadataJson["value"] = serializeVector(metadataContainer.intVector3Data.at(metadataKey)); break;
case MetadataType::IntVector4: metadataJson["type"] = "IntVector4"; metadataJson["value"] = serializeVector(metadataContainer.intVector4Data.at(metadataKey)); break;
case MetadataType::Float: metadataJson["type"] = "Float"; metadataJson["value"] = Json::Value(metadataContainer.floatData.at(metadataKey)); break;
case MetadataType::FloatVector2: metadataJson["type"] = "FloatVector2"; metadataJson["value"] = serializeVector(metadataContainer.floatVector2Data.at(metadataKey)); break;
case MetadataType::FloatVector3: metadataJson["type"] = "FloatVector3"; metadataJson["value"] = serializeVector(metadataContainer.floatVector3Data.at(metadataKey)); break;
case MetadataType::FloatVector4: metadataJson["type"] = "FloatVector4"; metadataJson["value"] = serializeVector(metadataContainer.floatVector4Data.at(metadataKey)); break;
case MetadataType::Double: metadataJson["type"] = "Double"; metadataJson["value"] = Json::Value(metadataContainer.doubleData.at(metadataKey)); break;
case MetadataType::DoubleVector2: metadataJson["type"] = "DoubleVector2"; metadataJson["value"] = serializeVector(metadataContainer.doubleVector2Data.at(metadataKey)); break;
case MetadataType::DoubleVector3: metadataJson["type"] = "DoubleVector3"; metadataJson["value"] = serializeVector(metadataContainer.doubleVector3Data.at(metadataKey)); break;
case MetadataType::DoubleVector4: metadataJson["type"] = "DoubleVector4"; metadataJson["value"] = serializeVector(metadataContainer.doubleVector4Data.at(metadataKey)); break;
case MetadataType::String: metadataJson["type"] = "String"; metadataJson["value"] = Json::Value(metadataContainer.stringData.at(metadataKey)); break;
case MetadataType::BLOB: metadataJson["type"] = "BLOB"; metadataJson["value"] = serializeBLOB(metadataContainer.blobData.at(metadataKey)); break;
}
assert(nChunkDimensionality == 2 || nChunkDimensionality == 3);
metadataJsonArray.append(metadataJson);
}
return metadataJsonArray;
}
std::string to_string(VolumeDataLayoutDescriptor::BrickSize brickSize)
{
switch(brickSize)
{
case VolumeDataLayoutDescriptor::BrickSize_32: return "BrickSize_32";
case VolumeDataLayoutDescriptor::BrickSize_64: return "BrickSize_64";
case VolumeDataLayoutDescriptor::BrickSize_128: return "BrickSize_128";
case VolumeDataLayoutDescriptor::BrickSize_256: return "BrickSize_256";
case VolumeDataLayoutDescriptor::BrickSize_512: return "BrickSize_512";
case VolumeDataLayoutDescriptor::BrickSize_1024: return "BrickSize_1024";
case VolumeDataLayoutDescriptor::BrickSize_2048: return "BrickSize_2048";
case VolumeDataLayoutDescriptor::BrickSize_4096: return "BrickSize_4096";
default: assert(0 && "Illegal brick size"); return "";
};
}
std::string to_string(VolumeDataLayoutDescriptor::LODLevels lodLevels)
{
switch(lodLevels)
{
case VolumeDataLayoutDescriptor::LODLevels_None: return "LODLevels_None";
case VolumeDataLayoutDescriptor::LODLevels_1: return "LODLevels_1";
case VolumeDataLayoutDescriptor::LODLevels_2: return "LODLevels_2";
case VolumeDataLayoutDescriptor::LODLevels_3: return "LODLevels_3";
case VolumeDataLayoutDescriptor::LODLevels_4: return "LODLevels_4";
case VolumeDataLayoutDescriptor::LODLevels_5: return "LODLevels_5";
case VolumeDataLayoutDescriptor::LODLevels_6: return "LODLevels_6";
case VolumeDataLayoutDescriptor::LODLevels_7: return "LODLevels_7";
case VolumeDataLayoutDescriptor::LODLevels_8: return "LODLevels_8";
case VolumeDataLayoutDescriptor::LODLevels_9: return "LODLevels_9";
case VolumeDataLayoutDescriptor::LODLevels_10: return "LODLevels_10";
case VolumeDataLayoutDescriptor::LODLevels_11: return "LODLevels_11";
case VolumeDataLayoutDescriptor::LODLevels_12: return "LODLevels_12";
default: assert(0 && "Illegal LOD levels"); return "";
};
}
Json::Value serializeVolumeDataLayout(VolumeDataLayout const &volumeDataLayout, MetadataContainer const &metadataContainer)
{
Json::Value root;
VolumeDataLayoutDescriptor
layoutDescriptor = volumeDataLayout.getLayoutDescriptor();
Json::Value layoutDescriptorJson;
layoutDescriptorJson["brickSize"] = to_string(layoutDescriptor.getBrickSize());
layoutDescriptorJson["negativeMargin"] = layoutDescriptor.getNegativeMargin();
layoutDescriptorJson["positiveMargin"] = layoutDescriptor.getPositiveMargin();
layoutDescriptorJson["brickSize2DMultiplier"] = layoutDescriptor.getBrickSizeMultiplier2D();
layoutDescriptorJson["lodLevels"] = to_string(layoutDescriptor.getLODLevels());
layoutDescriptorJson["create2DLODs"] = layoutDescriptor.isCreate2DLODs();
layoutDescriptorJson["forceFullResolutionDimension"] = layoutDescriptor.isForceFullResolutionDimension();
layoutDescriptorJson["fullResolutionDimension"] = layoutDescriptor.getFullResolutionDimension();
root["layoutDescriptor"] = layoutDescriptorJson;
Json::Value axisDescriptorsJson(Json::ValueType::arrayValue);
for(int dimension = 0, dimensionality = volumeDataLayout.getDimensionality(); dimension < dimensionality; dimension++)
{
axisDescriptorsJson.append(serializeAxisDescriptor(volumeDataLayout.getAxisDescriptor(dimension)));
}
root["axisDescriptors"] = axisDescriptorsJson;
Json::Value channelDescriptorsJson(Json::ValueType::arrayValue);
for(int channel = 0, channelCount = volumeDataLayout.getChannelCount(); channel < channelCount; channel++)
{
channelDescriptorsJson.append(serializeChannelDescriptor(volumeDataLayout.getChannelDescriptor(channel)));
}
root["channelDescriptors"] = channelDescriptorsJson;
root["metadata"] = serializeMetadata(metadataContainer);
return root;
}
int32_t physicalLODLevels = (nChunkDimensionality == 3 || handle.layoutDescriptor.isCreate2DLODs()) ? getLODCount(handle.layoutDescriptor) : 1;
int32_t brickSize = getInternalCubeSizeLOD0(handle.layoutDescriptor) * (nChunkDimensionality == 2 ? handle.layoutDescriptor.getBrickSizeMultiplier2D() : 1);
std::vector<uint8_t>
writeJson(Json::Value root)
{
std::vector<uint8_t>
result;
Json::StreamWriterBuilder wbuilder;
wbuilder["indentation"] = " ";