// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkAffineArray.h" #include "vtkAffineTypeFloat32Array.h" #include "vtkAffineTypeFloat64Array.h" #include "vtkAffineTypeInt16Array.h" #include "vtkAffineTypeInt32Array.h" #include "vtkAffineTypeInt64Array.h" #include "vtkAffineTypeInt8Array.h" #include "vtkAffineTypeUInt16Array.h" #include "vtkAffineTypeUInt32Array.h" #include "vtkAffineTypeUInt64Array.h" #include "vtkAffineTypeUInt8Array.h" #include "vtkArrayDispatch.h" #include "vtkBitArray.h" #include "vtkCharArray.h" #include "vtkConstantArray.h" #include "vtkConstantTypeFloat32Array.h" #include "vtkConstantTypeFloat64Array.h" #include "vtkConstantTypeInt16Array.h" #include "vtkConstantTypeInt32Array.h" #include "vtkConstantTypeInt64Array.h" #include "vtkConstantTypeInt8Array.h" #include "vtkConstantTypeUInt16Array.h" #include "vtkConstantTypeUInt32Array.h" #include "vtkConstantTypeUInt64Array.h" #include "vtkConstantTypeUInt8Array.h" #include "vtkDataArray.h" #include "vtkDataArrayRange.h" #include "vtkDeserializer.h" #include "vtkFloatArray.h" #include "vtkIdTypeArray.h" #include "vtkIntArray.h" #include "vtkInvoker.h" #include "vtkLongArray.h" #include "vtkLongLongArray.h" #include "vtkLookupTable.h" #include "vtkSerializer.h" #include "vtkSetGet.h" #include "vtkShortArray.h" #include "vtkTypeFloat32Array.h" #include "vtkTypeFloat64Array.h" #include "vtkTypeInt16Array.h" #include "vtkTypeInt32Array.h" #include "vtkTypeInt64Array.h" #include "vtkTypeInt8Array.h" #include "vtkTypeUInt16Array.h" #include "vtkTypeUInt32Array.h" #include "vtkTypeUInt64Array.h" #include "vtkTypeUInt8Array.h" #include "vtkUnsignedCharArray.h" #include "vtkUnsignedIntArray.h" #include "vtkUnsignedLongArray.h" #include "vtkUnsignedLongLongArray.h" #include "vtkUnsignedShortArray.h" // clang-format off #include "vtk_nlohmannjson.h" #include VTK_NLOHMANN_JSON(json.hpp) // clang-format on extern "C" { /** * Register the (de)serialization handlers of vtkDataArray subclasses * @param ser a vtkSerializer instance * @param deser a vtkDeserializer instance */ int RegisterHandlers_vtkDataArraySerDesHelper(void* ser, void* deser, void* invoker); } namespace { struct ArrayTypeInfo { const std::string Name; std::function New; const std::type_info& TypeInfo; }; #define TYPE_INFO_MACRO(className) \ { \ #className, className::New, typeid(className) \ } #define TTYPE_INFO_MACRO(className) \ { \ #className, className::New, typeid(className) \ } // clang-format off #define TEMPLATED_ARRAY_TYPES_INFO_MACRO(className) \ TTYPE_INFO_MACRO(className), \ TTYPE_INFO_MACRO(className), \ TTYPE_INFO_MACRO(className), \ TTYPE_INFO_MACRO(className), \ TTYPE_INFO_MACRO(className), \ TTYPE_INFO_MACRO(className), \ TTYPE_INFO_MACRO(className), \ TTYPE_INFO_MACRO(className), \ TTYPE_INFO_MACRO(className), \ TTYPE_INFO_MACRO(className), \ TTYPE_INFO_MACRO(className), \ TTYPE_INFO_MACRO(className), \ TTYPE_INFO_MACRO(className) #define CONCRETE_ARRAY_TYPES_INFO_MACRO(type) \ TYPE_INFO_MACRO(type##TypeInt8Array), \ TYPE_INFO_MACRO(type##TypeInt16Array), \ TYPE_INFO_MACRO(type##TypeInt32Array), \ TYPE_INFO_MACRO(type##TypeInt64Array), \ TYPE_INFO_MACRO(type##TypeUInt8Array), \ TYPE_INFO_MACRO(type##TypeUInt16Array), \ TYPE_INFO_MACRO(type##TypeUInt32Array), \ TYPE_INFO_MACRO(type##TypeUInt64Array), \ TYPE_INFO_MACRO(type##TypeFloat32Array), \ TYPE_INFO_MACRO(type##TypeFloat64Array) // The templated types should match those in the TEMPLATED_ARRAY_TYPES_INFO_MACRO. #define TEMPLATED_ARRAY_NAME_DEMANGLE_MACRO(className, ValueType, valueTypeName) \ template <> \ const char* GetDemangledClassNameFor##className(const char* templateArrayClassName) \ { \ if (!strcmp(templateArrayClassName, #className)) \ { \ return #className "<" #valueTypeName ">"; \ } \ vtkLogF(ERROR, "Specialization missing for " #className "<" #ValueType ">"); \ return nullptr; \ } #define TEMPLATED_ARRAY_NAMES_DEMANGLE_MACRO(className) \ template \ const char* GetDemangledClassNameFor##className(const char* templateArrayClassName) \ { \ (void)templateArrayClassName; \ vtkLogF(ERROR, "Specialization missing for " #className ""); \ return nullptr; \ } \ TEMPLATED_ARRAY_NAME_DEMANGLE_MACRO(className, char, char); \ TEMPLATED_ARRAY_NAME_DEMANGLE_MACRO(className, double, double); \ TEMPLATED_ARRAY_NAME_DEMANGLE_MACRO(className, float, float); \ TEMPLATED_ARRAY_NAME_DEMANGLE_MACRO(className, int, int); \ TEMPLATED_ARRAY_NAME_DEMANGLE_MACRO(className, long, long); \ TEMPLATED_ARRAY_NAME_DEMANGLE_MACRO(className, long long, long long); \ TEMPLATED_ARRAY_NAME_DEMANGLE_MACRO(className, short, short); \ TEMPLATED_ARRAY_NAME_DEMANGLE_MACRO(className, signed char, signed char); \ TEMPLATED_ARRAY_NAME_DEMANGLE_MACRO(className, unsigned char, unsigned char); \ TEMPLATED_ARRAY_NAME_DEMANGLE_MACRO(className, unsigned int, unsigned int); \ TEMPLATED_ARRAY_NAME_DEMANGLE_MACRO(className, unsigned long, unsigned long); \ TEMPLATED_ARRAY_NAME_DEMANGLE_MACRO(className, unsigned long long, unsigned long long); \ TEMPLATED_ARRAY_NAME_DEMANGLE_MACRO(className, unsigned short, unsigned short) std::vector ArrayTypes = { TYPE_INFO_MACRO(vtkBitArray), TYPE_INFO_MACRO(vtkCharArray), TYPE_INFO_MACRO(vtkDoubleArray), TYPE_INFO_MACRO(vtkFloatArray), TYPE_INFO_MACRO(vtkIdTypeArray), TYPE_INFO_MACRO(vtkIntArray), TYPE_INFO_MACRO(vtkLongArray), TYPE_INFO_MACRO(vtkLongLongArray), TYPE_INFO_MACRO(vtkShortArray), TYPE_INFO_MACRO(vtkSignedCharArray), TYPE_INFO_MACRO(vtkUnsignedCharArray), TYPE_INFO_MACRO(vtkUnsignedIntArray), TYPE_INFO_MACRO(vtkUnsignedLongArray), TYPE_INFO_MACRO(vtkUnsignedLongLongArray), TYPE_INFO_MACRO(vtkUnsignedShortArray), TEMPLATED_ARRAY_TYPES_INFO_MACRO(vtkAOSDataArrayTemplate), CONCRETE_ARRAY_TYPES_INFO_MACRO(vtk), TEMPLATED_ARRAY_TYPES_INFO_MACRO(vtkAffineArray), CONCRETE_ARRAY_TYPES_INFO_MACRO(vtkAffine), TEMPLATED_ARRAY_TYPES_INFO_MACRO(vtkConstantArray), CONCRETE_ARRAY_TYPES_INFO_MACRO(vtkConstant) }; typedef vtkTypeList::Create< vtkAffineArray, vtkAffineArray, vtkAffineArray, vtkAffineArray, vtkAffineArray, vtkAffineArray, vtkAffineArray, vtkAffineArray, vtkAffineArray, vtkAffineArray, vtkAffineArray, vtkAffineArray, vtkAffineArray> AffineArrays; typedef vtkTypeList::Create< vtkConstantArray, vtkConstantArray, vtkConstantArray, vtkConstantArray, vtkConstantArray, vtkConstantArray, vtkConstantArray, vtkConstantArray, vtkConstantArray, vtkConstantArray, vtkConstantArray, vtkConstantArray, vtkConstantArray> ConstantArrays; typedef vtkTypeList::Append::Result DispatchTypeList; // clang-format on TEMPLATED_ARRAY_NAMES_DEMANGLE_MACRO(vtkAOSDataArrayTemplate); TEMPLATED_ARRAY_NAMES_DEMANGLE_MACRO(vtkAffineArray); TEMPLATED_ARRAY_NAMES_DEMANGLE_MACRO(vtkConstantArray); void Serialize_Blob(vtkTypeUInt8Array* blob, nlohmann::json& state, vtkSerializer* serializer) { auto context = serializer->GetContext(); std::string hash; if (context->RegisterBlob(blob, hash)) { state["Hash"] = hash; } else { vtkErrorWithObjectMacro(context, << serializer->GetObjectDescription() << " failed to add blob " << blob->GetObjectDescription()); return; } } bool Deserialize_Blob( nlohmann::json& blob, const nlohmann::json& state, vtkDeserializer* deserializer) { auto* context = deserializer->GetContext(); const auto& hash = state["Hash"].get(); const auto& blobs = context->Blobs(); const auto blobIter = blobs.find(hash); if (blobIter == blobs.end()) { vtkErrorWithObjectMacro( context, << deserializer->GetObjectDescription() << " failed to find blob for hash=" << hash); return false; } if (!blobIter.value().is_binary()) { vtkErrorWithObjectMacro( context, << deserializer->GetObjectDescription() << " failed to find blob for hash=" << hash); return false; } blob = blobIter.value(); return true; } struct vtkDataArraySerializer { template void operator()( vtkAffineArray* array, nlohmann::json& state, vtkSerializer* vtkNotUsed(serializer)) { if (array == nullptr) { return; } // demangle and record the actual templated class name if (strstr(array->GetClassName(), "vtkImplicitArray")) { state["ClassName"] = GetDemangledClassNameForvtkAffineArray("vtkAffineArray"); } auto backend = array->GetBackend(); if (backend == nullptr) { vtkLogF(ERROR, "AffineArray backend is null"); return; } state["Slope"] = backend->Slope; state["Intercept"] = backend->Intercept; } template void operator()( vtkConstantArray* array, nlohmann::json& state, vtkSerializer* vtkNotUsed(serializer)) { if (array == nullptr) { return; } // demangle and record the actual templated class name if (strstr(array->GetClassName(), "vtkImplicitArray")) { state["ClassName"] = GetDemangledClassNameForvtkConstantArray("vtkConstantArray"); } auto backend = array->GetBackend(); if (backend == nullptr) { vtkLogF(ERROR, "ConstantArray backend is null"); return; } state["Value"] = backend->Value; } template void operator()( vtkAOSDataArrayTemplate* array, nlohmann::json& state, vtkSerializer* serializer) { if (array == nullptr) { return; } // demangle and record the actual templated class name prior to early outs // to ensure the deserializer on the other end can create the correct type. if (strstr(array->GetClassName(), "vtkAOSDataArrayTemplate")) { // demangle and record the actual templated class name state["ClassName"] = GetDemangledClassNameForvtkAOSDataArrayTemplate("vtkAOSDataArrayTemplate"); } if (!array->GetNumberOfValues()) { return; } auto blob = vtk::TakeSmartPointer(vtkTypeUInt8Array::New()); vtkIdType arrSize = array->GetNumberOfValues() * array->GetDataTypeSize(); blob->SetArray(reinterpret_cast(array->GetPointer(0)), arrSize, 1); Serialize_Blob(blob, state, serializer); if (auto lt = array->GetLookupTable()) { state["LookupTable"] = serializer->SerializeJSON(lt); } } void operator()(vtkDataArray* array, nlohmann::json& state, vtkSerializer* serializer) { if (array == nullptr) { return; } else if (!array->GetNumberOfValues()) { return; } auto blob = vtk::TakeSmartPointer(vtkTypeUInt8Array::New()); vtkIdType arrSize = array->GetNumberOfValues() * array->GetDataTypeSize(); blob->SetArray(reinterpret_cast(array->GetVoidPointer(0)), arrSize, 1); Serialize_Blob(blob, state, serializer); if (auto lt = array->GetLookupTable()) { state["LookupTable"] = serializer->SerializeJSON(lt); } } void operator()(vtkBitArray* array, nlohmann::json& state, vtkSerializer* serializer) { if (array == nullptr) { return; } else if (!array->GetNumberOfValues()) { return; } auto blob = vtk::TakeSmartPointer(vtkTypeUInt8Array::New()); vtkIdType arrSize = (array->GetNumberOfValues() + 7) / 8; state["NumberOfBits"] = array->GetNumberOfValues(); blob->SetArray(array->GetPointer(0), arrSize, 1); Serialize_Blob(blob, state, serializer); if (auto lt = array->GetLookupTable()) { state["LookupTable"] = serializer->SerializeJSON(lt); } } }; struct vtkDataArrayDeserializer { template void operator()(vtkAffineArray* array, const nlohmann::json& state, vtkDeserializer* vtkNotUsed(deserializer)) { ValueT slope = state["Slope"].get(); ValueT intercept = state["Intercept"].get(); array->SetBackend(std::make_shared>(slope, intercept)); } template void operator()(vtkConstantArray* array, const nlohmann::json& state, vtkDeserializer* vtkNotUsed(deserializer)) { ValueT value = state["Value"].get(); array->SetBackend(std::make_shared>(value)); } template void operator()(vtkAOSDataArrayTemplate* array, const nlohmann::json& state, vtkDeserializer* deserializer) { nlohmann::json blob; if (!Deserialize_Blob(blob, state, deserializer)) { return; } const auto& content = blob.get_binary(); const ValueT* c_ptr = reinterpret_cast(content.data()); auto src = const_cast(c_ptr); auto dst = array->GetPointer(0); std::copy(src, src + array->GetNumberOfValues(), dst); VTK_DESERIALIZE_VTK_OBJECT_FROM_STATE(LookupTable, vtkLookupTable, state, array, deserializer); } void operator()(vtkDataArray* array, const nlohmann::json& state, vtkDeserializer* deserializer) { nlohmann::json blob; if (!Deserialize_Blob(blob, state, deserializer)) { return; } const auto& content = blob.get_binary(); switch (array->GetDataType()) { vtkTemplateMacro(const VTK_TT* c_ptr = reinterpret_cast(content.data()); auto src = const_cast(c_ptr); auto dst = reinterpret_cast(array->GetVoidPointer(0)); std::copy(src, src + array->GetNumberOfValues(), dst)); } // nifty memory savings below, unfortunately, doesn't work correctly when there are point // scalars. // array->SetVoidArray(const_cast(c_ptr), array->GetNumberOfValues(), 1); VTK_DESERIALIZE_VTK_OBJECT_FROM_STATE(LookupTable, vtkLookupTable, state, array, deserializer); } void operator()(vtkBitArray* array, const nlohmann::json& state, vtkDeserializer* deserializer) { nlohmann::json blob; if (!Deserialize_Blob(blob, state, deserializer)) { return; } const auto& content = blob.get_binary(); std::copy(content.data(), content.data() + ((array->GetNumberOfValues() + 7) / 8), array->GetPointer(0)); array->SetNumberOfValues(state["NumberOfBits"]); VTK_DESERIALIZE_VTK_OBJECT_FROM_STATE(LookupTable, vtkLookupTable, state, array, deserializer); } }; } static nlohmann::json Serialize_vtkDataArray(vtkObjectBase* object, vtkSerializer* serializer) { auto da = vtkDataArray::SafeDownCast(object); if (!da) { return {}; } nlohmann::json state; if (auto* context = serializer->GetContext()) { const auto id = context->GetId(object); if (id > 0) { state = context->GetState(id); if (!state.empty()) { const auto stateMTime = state.at("MTime").get(); if (da->GetMTime() <= stateMTime) { return state; } vtkVLog(serializer->GetSerializerLogVerbosity(), << "Reserialize because object-mtime=" << da->GetMTime() << " >= state-mtime=" << stateMTime); } } } else { vtkErrorWithObjectMacro(serializer, "Serializer does not have a marshal context!"); return state; } if (auto superSerializer = serializer->GetHandler(typeid(vtkDataArray::Superclass))) { state = superSerializer(da, serializer); } vtkDataArraySerializer serializeWorker; using Dispatch = vtkArrayDispatch::DispatchByArray; if (!Dispatch::Execute(da, serializeWorker, state, serializer)) { serializeWorker(da, state, serializer); } auto& superClasses = state["SuperClassNames"]; superClasses.push_back("vtkAbstractArray"); superClasses.push_back("vtkDataArray"); for (const auto& arrayType : ArrayTypes) { if (da->IsA(arrayType.Name.c_str()) && da->GetClassName() != arrayType.Name) { superClasses.push_back(arrayType.Name); } } return state; } static void Deserialize_vtkDataArray( const nlohmann::json& state, vtkObjectBase* object, vtkDeserializer* deserializer) { if (object == nullptr) { return; } if (const auto superDeserializer = deserializer->GetHandler(typeid(vtkDataArray::Superclass))) { superDeserializer(state, object, deserializer); } auto* da = vtkDataArray::SafeDownCast(object); if (!da->GetNumberOfValues()) { return; } vtkDataArrayDeserializer deserializeWorker; using Dispatch = vtkArrayDispatch::DispatchByArray; if (!Dispatch::Execute(da, deserializeWorker, state, deserializer)) { deserializeWorker(da, state, deserializer); } } int RegisterHandlers_vtkDataArraySerDesHelper(void* ser, void* deser, void* invoker) { int success = 0; if (auto* asObjectBase = static_cast(ser)) { if (auto* serializer = vtkSerializer::SafeDownCast(asObjectBase)) { for (auto& arrayType : ArrayTypes) { serializer->RegisterHandler(arrayType.TypeInfo, Serialize_vtkDataArray); } success = 1; } } if (auto* asObjectBase = static_cast(deser)) { if (auto* deserializer = vtkDeserializer::SafeDownCast(asObjectBase)) { for (auto& arrayType : ArrayTypes) { deserializer->RegisterConstructor(arrayType.Name, arrayType.New); deserializer->RegisterHandler(arrayType.TypeInfo, Deserialize_vtkDataArray); } success = 1; } } // copy invokers if (auto* asObjectBase = static_cast(invoker)) { if (auto* invokerObject = vtkInvoker::SafeDownCast(asObjectBase)) { for (auto& arrayType : ArrayTypes) { invokerObject->RegisterHandler( arrayType.TypeInfo, invokerObject->GetHandler(typeid(vtkDataArray))); } } } return success; }