// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-FileCopyrightText: Copyright 2008 Sandia Corporation // SPDX-License-Identifier: LicenseRef-BSD-3-Clause-Sandia-USGov #include "vtkArrayReader.h" #include "vtkArrayData.h" #include "vtkCommand.h" #include "vtkDenseArray.h" #include "vtkObjectFactory.h" #include "vtkSmartPointer.h" #include "vtkSparseArray.h" #include "vtksys/FStream.hxx" #include #include #include VTK_ABI_NAMESPACE_BEGIN vtkStandardNewMacro(vtkArrayReader); namespace { template void ExtractValue(istream& stream, ValueT& value) { stream >> value; } void ExtractValue(istream& stream, vtkStdString& value) { std::getline(stream, value); vtkStdString::size_type begin, end; begin = 0; end = value.size(); while ((begin < end) && isspace(value[begin])) begin++; while ((begin < end) && isspace(value[end - 1])) end--; value = value.substr(begin, end); } void ReadHeader( istream& stream, vtkArrayExtents& extents, vtkArrayExtents::SizeT& non_null_size, vtkArray* array) { if (!array) throw std::runtime_error("Missing array."); // Load the array name ... std::string name; std::getline(stream, name); array->SetName(name); // Load array extents ... std::string extents_string; std::getline(stream, extents_string); std::istringstream extents_buffer(extents_string); vtkArrayExtents::CoordinateT extent; std::vector temp_extents; for (extents_buffer >> extent; extents_buffer; extents_buffer >> extent) temp_extents.push_back(extent); extents.SetDimensions(0); while (temp_extents.size() > 1) { const vtkArrayExtents::CoordinateT begin = temp_extents.front(); temp_extents.erase(temp_extents.begin()); const vtkArrayExtents::CoordinateT end = temp_extents.front(); temp_extents.erase(temp_extents.begin()); extents.Append(vtkArrayRange(begin, end)); } if (extents.GetDimensions() < 1) throw std::runtime_error("Array cannot have fewer than one dimension."); if (temp_extents.empty()) throw std::runtime_error("Missing non null size."); non_null_size = temp_extents.back(); array->Resize(extents); // Load dimension-labels ... for (vtkArrayExtents::DimensionT i = 0; i != extents.GetDimensions(); ++i) { std::string label; std::getline(stream, label); array->SetDimensionLabel(i, label); } } void ReadEndianOrderMark(istream& stream, bool& swap_endian) { // Load the endian-order mark ... vtkTypeUInt32 endian_order = 0; stream.read(reinterpret_cast(&endian_order), sizeof(endian_order)); swap_endian = endian_order != 0x12345678; } template vtkSparseArray* ReadSparseArrayBinary(istream& stream) { // Create the array ... vtkSmartPointer> array = vtkSmartPointer>::New(); // Read the file header ... vtkArrayExtents extents; vtkArrayExtents::SizeT non_null_size = 0; bool swap_endian = false; ReadHeader(stream, extents, non_null_size, array); ReadEndianOrderMark(stream, swap_endian); // Read the array nullptr value ... ValueT null_value; stream.read(reinterpret_cast(&null_value), sizeof(ValueT)); array->SetNullValue(null_value); // Read array coordinates ... array->ReserveStorage(non_null_size); for (vtkArray::DimensionT i = 0; i != array->GetDimensions(); ++i) { stream.read(reinterpret_cast(array->GetCoordinateStorage(i)), non_null_size * sizeof(vtkArray::CoordinateT)); } // Read array values ... stream.read(reinterpret_cast(array->GetValueStorage()), non_null_size * sizeof(ValueT)); array->Register(nullptr); return array; } template <> vtkSparseArray* ReadSparseArrayBinary(istream& stream) { // Create the array ... vtkSmartPointer> array = vtkSmartPointer>::New(); // Read the file header ... vtkArrayExtents extents; vtkArrayExtents::SizeT non_null_size = 0; bool swap_endian = false; ReadHeader(stream, extents, non_null_size, array); ReadEndianOrderMark(stream, swap_endian); // Read the array nullptr value ... std::string null_value; for (int character = stream.get(); stream; character = stream.get()) { if (character == 0) { array->SetNullValue(null_value); break; } else { null_value += static_cast(character); } } // Read array coordinates ... array->ReserveStorage(non_null_size); for (vtkArray::DimensionT i = 0; i != array->GetDimensions(); ++i) { stream.read(reinterpret_cast(array->GetCoordinateStorage(i)), non_null_size * sizeof(vtkArray::CoordinateT)); } // Read array values ... std::string buffer; vtkArray::SizeT n = 0; for (int character = stream.get(); stream; character = stream.get()) { if (character == 0) { array->SetValueN(n++, buffer); buffer.resize(0); } else { buffer += static_cast(character); } } array->Register(nullptr); return array; } template vtkDenseArray* ReadDenseArrayBinary(istream& stream) { // Create the array ... vtkSmartPointer> array = vtkSmartPointer>::New(); // Read the file header ... vtkArrayExtents extents; vtkArrayExtents::SizeT non_null_size = 0; bool swap_endian = false; ReadHeader(stream, extents, non_null_size, array); ReadEndianOrderMark(stream, swap_endian); // Read array values ... stream.read(reinterpret_cast(array->GetStorage()), non_null_size * sizeof(ValueT)); if (stream.eof()) throw std::runtime_error("Premature end-of-file."); if (stream.bad()) throw std::runtime_error("Error while reading file."); array->Register(nullptr); return array; } template <> vtkDenseArray* ReadDenseArrayBinary(istream& stream) { // Create the array ... vtkSmartPointer> array = vtkSmartPointer>::New(); // Read the file header ... vtkArrayExtents extents; vtkArrayExtents::SizeT non_null_size = 0; bool swap_endian = false; ReadHeader(stream, extents, non_null_size, array); ReadEndianOrderMark(stream, swap_endian); // Read array values ... std::string buffer; vtkArray::SizeT n = 0; for (int character = stream.get(); stream; character = stream.get()) { if (character == 0) { array->SetValueN(n++, buffer); buffer.resize(0); } else { buffer += static_cast(character); } } array->Register(nullptr); return array; } template vtkSparseArray* ReadSparseArrayAscii(istream& stream) { // Create the array ... vtkSmartPointer> array = vtkSmartPointer>::New(); // Read the stream header ... vtkArrayExtents extents; vtkArrayExtents::SizeT non_null_size = 0; ReadHeader(stream, extents, non_null_size, array); if (non_null_size > extents.GetSize()) throw std::runtime_error("Too many values for a sparse array."); // Read the array nullptr value ... std::string line_buffer; std::getline(stream, line_buffer); if (!stream) throw std::runtime_error("Premature end-of-stream reading nullptr value."); std::istringstream line_stream(line_buffer); ValueT null_value; ExtractValue(line_stream, null_value); if (!line_stream) throw std::runtime_error("Missing nullptr value."); array->SetNullValue(null_value); // Setup storage for the stream contents ... array->ReserveStorage(non_null_size); std::vector coordinates(array->GetDimensions()); for (vtkArray::DimensionT j = 0; j != array->GetDimensions(); ++j) coordinates[j] = array->GetCoordinateStorage(j); ValueT* value = array->GetValueStorage(); // Read the stream contents ... vtkArray::SizeT value_count = 0; for (; value_count < non_null_size; ++value_count) { std::getline(stream, line_buffer); if (!stream) break; line_stream.clear(); line_stream.str(line_buffer); for (vtkArray::DimensionT j = 0; j != array->GetDimensions(); ++j) { line_stream >> *(coordinates[j] + value_count); if (!extents[j].Contains(*(coordinates[j] + value_count))) throw std::runtime_error("Coordinate out-of-bounds."); if (!line_stream) throw std::runtime_error("Missing coordinate."); } ExtractValue(line_stream, *(value + value_count)); if (!line_stream) throw std::runtime_error("Missing value."); } // Ensure we loaded enough values ... if (value_count != non_null_size) throw std::runtime_error("Stream doesn't contain enough values."); array->Register(nullptr); return array; } template vtkDenseArray* ReadDenseArrayAscii(istream& stream) { // Create the array ... vtkSmartPointer> array = vtkSmartPointer>::New(); // Read the file header ... vtkArrayExtents extents; vtkArrayExtents::SizeT non_null_size = 0; ReadHeader(stream, extents, non_null_size, array); if (non_null_size != extents.GetSize()) throw std::runtime_error("Incorrect number of values for a dense array."); // Read the file contents ... ValueT value; vtkArray::SizeT n = 0; vtkArrayCoordinates coordinates; for (; n < non_null_size; ++n) { ExtractValue(stream, value); if (!stream) break; extents.GetRightToLeftCoordinatesN(n, coordinates); array->SetValue(coordinates, value); } if (n != non_null_size) throw std::runtime_error("Stream doesn't contain enough values."); // If there is more in the stream (e.g. in vtkArrayDataReader), // eat the newline so the stream is ready for the next vtkArray. if (stream) { stream.get(); } array->Register(nullptr); return array; } } // End anonymous namespace vtkArrayReader::vtkArrayReader() : FileName(nullptr) { this->SetNumberOfInputPorts(0); this->ReadFromInputString = false; } vtkArrayReader::~vtkArrayReader() { this->SetFileName(nullptr); } void vtkArrayReader::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "FileName: " << (this->FileName ? this->FileName : "(none)") << endl; os << indent << "InputString: " << this->InputString << endl; os << indent << "ReadFromInputString: " << (this->ReadFromInputString ? "on" : "off") << endl; } void vtkArrayReader::SetInputString(const vtkStdString& string) { this->InputString = string; this->Modified(); } vtkStdString vtkArrayReader::GetInputString() { return this->InputString; } int vtkArrayReader::RequestData( vtkInformation*, vtkInformationVector**, vtkInformationVector* outputVector) { try { vtkArray* array = nullptr; if (this->ReadFromInputString) { array = vtkArrayReader::Read(this->InputString); } else { if (!this->FileName) throw std::runtime_error("FileName not set."); vtksys::ifstream file(this->FileName, std::ios::binary); array = vtkArrayReader::Read(file); } if (!array) throw std::runtime_error("Error reading array."); vtkArrayData* const array_data = vtkArrayData::GetData(outputVector); array_data->ClearArrays(); array_data->AddArray(array); array->Delete(); return 1; } catch (std::exception& e) { vtkErrorMacro(<< e.what()); } return 0; } vtkArray* vtkArrayReader::Read(const vtkStdString& str) { std::istringstream iss(str); return vtkArrayReader::Read(iss); } vtkArray* vtkArrayReader::Read(istream& stream) { try { // Read enough of the file header to identify the type ... std::string header_string; std::getline(stream, header_string); std::istringstream header_buffer(header_string); std::string header_magic; std::string header_type; header_buffer >> header_magic >> header_type; // Read input file type, binary or ascii std::string header_file_string; std::string header_file_type; std::getline(stream, header_file_string); std::istringstream header_file_type_buffer(header_file_string); header_file_type_buffer >> header_file_type; bool read_binary = false; if (header_file_type == "binary") { read_binary = true; } else if (header_file_type != "ascii") { throw std::runtime_error("Unknown file type: " + header_file_type); } if (header_magic == "vtk-sparse-array") { if (header_type == "integer") { return (read_binary ? ReadSparseArrayBinary(stream) : ReadSparseArrayAscii(stream)); } else if (header_type == "double") { return (read_binary ? ReadSparseArrayBinary(stream) : ReadSparseArrayAscii(stream)); } else if ((header_type == "string") || header_type == "unicode-string") { return (read_binary ? ReadSparseArrayBinary(stream) : ReadSparseArrayAscii(stream)); } else { throw std::runtime_error("Unknown array type: " + header_type); } } else if (header_magic == "vtk-dense-array") { if (header_type == "integer") { return (read_binary ? ReadDenseArrayBinary(stream) : ReadDenseArrayAscii(stream)); } else if (header_type == "double") { return ( read_binary ? ReadDenseArrayBinary(stream) : ReadDenseArrayAscii(stream)); } else if ((header_type == "string") || (header_type == "unicode-string")) { return (read_binary ? ReadDenseArrayBinary(stream) : ReadDenseArrayAscii(stream)); } else { throw std::runtime_error("Unknown array type: " + header_type); } } else { throw std::runtime_error("Unknown file type: " + header_magic); } } catch (std::exception& e) { vtkGenericWarningMacro(<< e.what()); } return nullptr; } VTK_ABI_NAMESPACE_END