/*========================================================================= Program: Visualization Toolkit Module: vtkXMLDataParser.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkXMLDataParser.h" #include "vtkBase64InputStream.h" #include "vtkByteSwap.h" #include "vtkCommand.h" #include "vtkDataCompressor.h" #include "vtkEndian.h" #include "vtkInputStream.h" #include "vtkObjectFactory.h" #include "vtkXMLDataElement.h" #define vtkXMLDataHeaderPrivate_DoNotInclude #include "vtkXMLDataHeaderPrivate.h" #undef vtkXMLDataHeaderPrivate_DoNotInclude #include #include #include #include #include #include #include "vtkXMLUtilities.h" vtkStandardNewMacro(vtkXMLDataParser); vtkCxxSetObjectMacro(vtkXMLDataParser, Compressor, vtkDataCompressor); //------------------------------------------------------------------------------ vtkXMLDataParser::vtkXMLDataParser() { this->NumberOfOpenElements = 0; this->OpenElementsSize = 10; this->OpenElements = new vtkXMLDataElement*[this->OpenElementsSize]; this->RootElement = nullptr; this->AppendedDataPosition = 0; this->AppendedDataMatched = 0; this->DataStream = nullptr; this->InlineDataStream = vtkBase64InputStream::New(); this->AppendedDataStream = vtkBase64InputStream::New(); this->BlockCompressedSizes = nullptr; this->BlockStartOffsets = nullptr; this->Compressor = nullptr; this->AsciiDataBuffer = nullptr; this->AsciiDataBufferLength = 0; this->AsciiDataPosition = 0; this->Abort = 0; this->Progress = 0; // Default byte order to that of this machine. #ifdef VTK_WORDS_BIGENDIAN this->ByteOrder = vtkXMLDataParser::BigEndian; #else this->ByteOrder = vtkXMLDataParser::LittleEndian; #endif this->HeaderType = 32; this->AttributesEncoding = VTK_ENCODING_NONE; // Have specialized methods for reading array data both inline or // appended, however typical tags may use the more general CharacterData // methods. this->IgnoreCharacterData = 0; } //------------------------------------------------------------------------------ vtkXMLDataParser::~vtkXMLDataParser() { this->FreeAllElements(); delete[] this->OpenElements; this->InlineDataStream->Delete(); this->AppendedDataStream->Delete(); delete[] this->BlockCompressedSizes; delete[] this->BlockStartOffsets; this->SetCompressor(nullptr); if (this->AsciiDataBuffer) { this->FreeAsciiBuffer(); } } //------------------------------------------------------------------------------ void vtkXMLDataParser::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "AppendedDataPosition: " << this->AppendedDataPosition << "\n"; if (this->RootElement) { this->RootElement->PrintXML(os, indent); } if (this->Compressor) { os << indent << "Compressor: " << this->Compressor << "\n"; } else { os << indent << "Compressor: (none)\n"; } os << indent << "Progress: " << this->Progress << "\n"; os << indent << "Abort: " << this->Abort << "\n"; os << indent << "AttributesEncoding: " << this->AttributesEncoding << "\n"; } //------------------------------------------------------------------------------ int vtkXMLDataParser::Parse() { // Delete any elements left from previous parsing. this->FreeAllElements(); // Parse the input from the stream. int result = this->Superclass::Parse(); // Check that the input is okay. if (result && !this->CheckPrimaryAttributes()) { result = 0; } return result; } //------------------------------------------------------------------------------ int vtkXMLDataParser::Parse(const char*) { vtkErrorMacro("Parsing from a string is not supported."); return 0; } //------------------------------------------------------------------------------ int vtkXMLDataParser::Parse(const char*, unsigned int) { vtkErrorMacro("Parsing from a string is not supported."); return 0; } //------------------------------------------------------------------------------ void vtkXMLDataParser::StartElement(const char* name, const char** atts) { vtkXMLDataElement* element = vtkXMLDataElement::New(); element->SetName(name); element->SetXMLByteIndex(this->GetXMLByteIndex()); vtkXMLUtilities::ReadElementFromAttributeArray(element, atts, this->AttributesEncoding); const char* id = element->GetAttribute("id"); if (id) { element->SetId(id); } this->PushOpenElement(element); if (strcmp(name, "AppendedData") == 0) { // This is the AppendedData element. this->FindAppendedDataPosition(); // Switch to raw decoder if necessary. const char* encoding = element->GetAttribute("encoding"); if (encoding && (strcmp(encoding, "raw") == 0)) { this->AppendedDataStream->Delete(); this->AppendedDataStream = vtkInputStream::New(); } } } //------------------------------------------------------------------------------ void vtkXMLDataParser::SeekInlineDataPosition(vtkXMLDataElement* element) { istream* stream = this->GetStream(); if (!element->GetInlineDataPosition()) { // Scan for the start of the actual inline data. char c = 0; stream->clear(stream->rdstate() & ~ios::eofbit); stream->clear(stream->rdstate() & ~ios::failbit); this->SeekG(element->GetXMLByteIndex()); while (stream->get(c) && (c != '>')) { ; } while (stream->get(c) && vtkXMLDataElement::IsSpace(c)) { ; } vtkTypeInt64 pos = this->TellG(); element->SetInlineDataPosition(pos - 1); } // Seek to the data position. this->SeekG(element->GetInlineDataPosition()); } //------------------------------------------------------------------------------ void vtkXMLDataParser::EndElement(const char*) { vtkXMLDataElement* finished = this->PopOpenElement(); unsigned int numOpen = this->NumberOfOpenElements; if (numOpen > 0) { this->OpenElements[numOpen - 1]->AddNestedElement(finished); finished->Delete(); } else { this->RootElement = finished; } } //------------------------------------------------------------------------------ int vtkXMLDataParser::ParsingComplete() { // If we have reached the appended data section, we stop parsing. // This prevents the XML parser from having to walk over the entire // appended data section. if (this->AppendedDataPosition) { return 1; } return this->Superclass::ParsingComplete(); } //------------------------------------------------------------------------------ int vtkXMLDataParser::CheckPrimaryAttributes() { const char* byte_order = this->RootElement->GetAttribute("byte_order"); if (byte_order) { if (strcmp(byte_order, "BigEndian") == 0) { this->ByteOrder = vtkXMLDataParser::BigEndian; } else if (strcmp(byte_order, "LittleEndian") == 0) { this->ByteOrder = vtkXMLDataParser::LittleEndian; } else { vtkErrorMacro("Unsupported byte_order=\"" << byte_order << "\""); return 0; } } if (const char* header_type = this->RootElement->GetAttribute("header_type")) { if (strcmp(header_type, "UInt32") == 0) { this->HeaderType = 32; } else if (strcmp(header_type, "UInt64") == 0) { this->HeaderType = 64; } else { vtkErrorMacro("Unsupported header_type=\"" << header_type << "\""); return 0; } } return 1; } //------------------------------------------------------------------------------ void vtkXMLDataParser::FindAppendedDataPosition() { // Clear stream fail and eof bits. We may have already read past // the end of the stream when processing the AppendedData element. this->Stream->clear(this->Stream->rdstate() & ~ios::failbit); this->Stream->clear(this->Stream->rdstate() & ~ios::eofbit); // Scan for the start of the actual appended data. char c = 0; vtkTypeInt64 returnPosition = this->TellG(); this->SeekG(this->GetXMLByteIndex()); while (this->Stream->get(c) && (c != '>')) { ; } while (this->Stream->get(c) && vtkXMLDataParser::IsSpace(c)) { ; } // Store the start of the appended data. We skip the first // character because it is always a "_". this->AppendedDataPosition = this->TellG(); // If first character was not an underscore, assume it is part of // the data. if (c != '_') { vtkWarningMacro("First character in AppendedData is ASCII value " << int(c) << ", not '_'. Scan for first character " << "started from file position " << this->GetXMLByteIndex() << ". The return position is " << returnPosition << "."); --this->AppendedDataPosition; } // Restore the stream position. this->SeekG(returnPosition); } //------------------------------------------------------------------------------ void vtkXMLDataParser::PushOpenElement(vtkXMLDataElement* element) { if (this->NumberOfOpenElements == this->OpenElementsSize) { unsigned int newSize = this->OpenElementsSize * 2; vtkXMLDataElement** newOpenElements = new vtkXMLDataElement*[newSize]; unsigned int i; for (i = 0; i < this->NumberOfOpenElements; ++i) { newOpenElements[i] = this->OpenElements[i]; } delete[] this->OpenElements; this->OpenElements = newOpenElements; this->OpenElementsSize = newSize; } unsigned int pos = this->NumberOfOpenElements++; this->OpenElements[pos] = element; } //------------------------------------------------------------------------------ vtkXMLDataElement* vtkXMLDataParser::PopOpenElement() { if (this->NumberOfOpenElements > 0) { --this->NumberOfOpenElements; return this->OpenElements[this->NumberOfOpenElements]; } return nullptr; } //------------------------------------------------------------------------------ void vtkXMLDataParser::FreeAllElements() { while (this->NumberOfOpenElements > 0) { --this->NumberOfOpenElements; this->OpenElements[this->NumberOfOpenElements]->Delete(); this->OpenElements[this->NumberOfOpenElements] = nullptr; } if (this->RootElement) { this->RootElement->Delete(); this->RootElement = nullptr; } } //------------------------------------------------------------------------------ int vtkXMLDataParser::ParseBuffer(const char* buffer, unsigned int count) { // Parsing must stop when "AppendedDataMatched; while (s != end) { char c = *s++; if (c == pattern[matched]) { if (++matched == length) { break; } } else { matched = (c == pattern[0]) ? 1 : 0; } } this->AppendedDataMatched = matched; // Parse as much of the buffer as is safe. if (!this->Superclass::ParseBuffer(buffer, s - buffer)) { return 0; } // If we have reached the appended data, artificially finish the // document. if (matched == length) { // Parse the rest of the element's opening tag. const char* t = s; char prev = 0; while ((t != end) && (*t != '>')) { ++t; } if (!this->Superclass::ParseBuffer(s, t - s)) { return 0; } if (t > s) { prev = *(t - 1); } if (t == end) { // Scan for the real end of the element's opening tag. char c = 0; while (this->Stream->get(c) && (c != '>')) { prev = c; if (!this->Superclass::ParseBuffer(&c, 1)) { return 0; } } } // Artificially end the AppendedData element. if (prev != '/') { if (!this->Superclass::ParseBuffer("/", 1)) { return 0; } } if (!this->Superclass::ParseBuffer(">", 1)) { return 0; } // Artificially end the VTKFile element. const char finish[] = "\n\n"; if (!this->Superclass::ParseBuffer(finish, sizeof(finish) - 1)) { return 0; } } return 1; } //------------------------------------------------------------------------------ template size_t vtkXMLDataParserGetWordTypeSize(T*) { return sizeof(T); } //------------------------------------------------------------------------------ size_t vtkXMLDataParser::GetWordTypeSize(int wordType) { size_t size = 1; switch (wordType) { vtkTemplateMacro(size = vtkXMLDataParserGetWordTypeSize(static_cast(nullptr))); case VTK_BIT: size = 1; break; default: { vtkWarningMacro("Unsupported data type: " << wordType); } break; } return size; } //------------------------------------------------------------------------------ void vtkXMLDataParser::PerformByteSwap(void* data, size_t numWords, size_t wordSize) { char* ptr = static_cast(data); if (this->ByteOrder == vtkXMLDataParser::BigEndian) { switch (wordSize) { case 1: break; case 2: vtkByteSwap::Swap2BERange(ptr, numWords); break; case 4: vtkByteSwap::Swap4BERange(ptr, numWords); break; case 8: vtkByteSwap::Swap8BERange(ptr, numWords); break; default: vtkErrorMacro("Unsupported data type size " << wordSize); } } else { switch (wordSize) { case 1: break; case 2: vtkByteSwap::Swap2LERange(ptr, numWords); break; case 4: vtkByteSwap::Swap4LERange(ptr, numWords); break; case 8: vtkByteSwap::Swap8LERange(ptr, numWords); break; default: vtkErrorMacro("Unsupported data type size " << wordSize); } } } //------------------------------------------------------------------------------ int vtkXMLDataParser::ReadCompressionHeader() { std::unique_ptr ch(vtkXMLDataHeader::New(this->HeaderType, 3)); this->DataStream->StartReading(); // Read the standard part of the header. size_t const headerSize = ch->DataSize(); size_t r = this->DataStream->Read(ch->Data(), headerSize); if (r < headerSize) { vtkErrorMacro("Error reading beginning of compression header. Read " << r << " of " << headerSize << " bytes."); return 0; } // Byte swap the header to make sure the values are correct. this->PerformByteSwap(ch->Data(), ch->WordCount(), ch->WordSize()); // Get the standard values. this->NumberOfBlocks = size_t(ch->Get(0)); this->BlockUncompressedSize = size_t(ch->Get(1)); this->PartialLastBlockUncompressedSize = size_t(ch->Get(2)); // Allocate the size and offset parts of the header. ch->Resize(this->NumberOfBlocks); delete[] this->BlockCompressedSizes; this->BlockCompressedSizes = nullptr; delete[] this->BlockStartOffsets; this->BlockStartOffsets = nullptr; if (this->NumberOfBlocks > 0) { this->BlockCompressedSizes = new size_t[this->NumberOfBlocks]; this->BlockStartOffsets = new vtkTypeInt64[this->NumberOfBlocks]; // Read the compressed block sizes. size_t len = ch->DataSize(); if (this->DataStream->Read(ch->Data(), len) < len) { vtkErrorMacro("Error reading compression header."); return 0; } // Byte swap the sizes to make sure the values are correct. this->PerformByteSwap(ch->Data(), ch->WordCount(), ch->WordSize()); } this->DataStream->EndReading(); // Use the compressed block sizes to calculate the starting offset // of each block. vtkTypeInt64 offset = 0; for (size_t i = 0; i < this->NumberOfBlocks; ++i) { size_t const sz = size_t(ch->Get(i)); this->BlockCompressedSizes[i] = sz; this->BlockStartOffsets[i] = offset; offset += sz; } return 1; } //------------------------------------------------------------------------------ size_t vtkXMLDataParser::FindBlockSize(vtkTypeUInt64 block) { if (block < this->NumberOfBlocks - (this->PartialLastBlockUncompressedSize ? 1 : 0)) { return this->BlockUncompressedSize; } else { return this->PartialLastBlockUncompressedSize; } } //------------------------------------------------------------------------------ int vtkXMLDataParser::ReadBlock(vtkTypeUInt64 block, unsigned char* buffer) { size_t uncompressedSize = this->FindBlockSize(block); size_t compressedSize = this->BlockCompressedSizes[block]; if (!this->DataStream->Seek(this->BlockStartOffsets[block])) { return 0; } unsigned char* readBuffer = new unsigned char[compressedSize]; if (this->DataStream->Read(readBuffer, compressedSize) < compressedSize) { delete[] readBuffer; return 0; } size_t result = this->Compressor->Uncompress(readBuffer, compressedSize, buffer, uncompressedSize); delete[] readBuffer; return result > 0; } //------------------------------------------------------------------------------ unsigned char* vtkXMLDataParser::ReadBlock(vtkTypeUInt64 block) { unsigned char* decompressBuffer = new unsigned char[this->FindBlockSize(block)]; if (!this->ReadBlock(block, decompressBuffer)) { delete[] decompressBuffer; return nullptr; } return decompressBuffer; } //------------------------------------------------------------------------------ size_t vtkXMLDataParser::ReadUncompressedData( unsigned char* data, vtkTypeUInt64 startWord, size_t numWords, size_t wordSize) { // First read the length of the data. std::unique_ptr uh(vtkXMLDataHeader::New(this->HeaderType, 1)); size_t const headerSize = uh->DataSize(); size_t r = this->DataStream->Read(uh->Data(), headerSize); if (r < headerSize) { vtkErrorMacro("Error reading uncompressed binary data header. " "Read " << r << " of " << headerSize << " bytes."); return 0; } this->PerformByteSwap(uh->Data(), uh->WordCount(), uh->WordSize()); vtkTypeUInt64 rsize = uh->Get(0); // Adjust the size to be a multiple of the wordSize by taking // advantage of integer division. This will only change the value // when the input file is invalid. vtkTypeUInt64 size = (rsize / wordSize) * wordSize; // Convert the start/length into bytes. vtkTypeUInt64 offset = startWord * wordSize; size_t length = numWords * wordSize; // Make sure the begin/end offsets fall within total size. if (offset > size) { return 0; } vtkTypeUInt64 end = offset + length; if (end > size) { end = size; } length = end - offset; // Read the data. if (!this->DataStream->Seek(headerSize + offset)) { return 0; } // Read data in 2MB blocks and report progress. size_t const blockSize = 2097152; size_t left = length; unsigned char* p = data; this->UpdateProgress(0); while (left > 0 && !this->Abort) { // Read this block. size_t n = (blockSize < left) ? blockSize : left; if (!this->DataStream->Read(p, n)) { return 0; } // Byte swap this block. Note that n will always be an integer // multiple of the word size. this->PerformByteSwap(p, n / wordSize, wordSize); // Update pointer and counter. p += n; left -= n; // Report progress. this->UpdateProgress(float(p - data) / length); } this->UpdateProgress(1); return length / wordSize; } //------------------------------------------------------------------------------ size_t vtkXMLDataParser::ReadCompressedData( unsigned char* data, vtkTypeUInt64 startWord, size_t numWords, size_t wordSize) { // Make sure there are data. if (numWords == 0) { return 0; } // Find the begin and end offsets into the data. vtkTypeUInt64 beginOffset = startWord * wordSize; vtkTypeUInt64 endOffset = beginOffset + numWords * wordSize; // Find the total size of the data. vtkTypeUInt64 totalSize = this->NumberOfBlocks * this->BlockUncompressedSize; if (this->PartialLastBlockUncompressedSize) { totalSize -= this->BlockUncompressedSize; totalSize += this->PartialLastBlockUncompressedSize; } // Make sure there's even data to be read if (totalSize == 0) { return 0; } // Adjust the size to be a multiple of the wordSize by taking // advantage of integer division. This will only change the value // when the input file is invalid. totalSize = (totalSize / wordSize) * wordSize; // Make sure the begin/end offsets fall within the total size. if (beginOffset > totalSize) { return 0; } if (endOffset > totalSize) { endOffset = totalSize; } // Find the range of compression blocks to read. vtkTypeUInt64 firstBlock = beginOffset / this->BlockUncompressedSize; vtkTypeUInt64 lastBlock = endOffset / this->BlockUncompressedSize; // Find the offset into the first block where the data begin. size_t beginBlockOffset = beginOffset - firstBlock * this->BlockUncompressedSize; // Find the offset into the last block where the data end. size_t endBlockOffset = endOffset - lastBlock * this->BlockUncompressedSize; this->UpdateProgress(0); if (firstBlock == lastBlock) { // Everything fits in one block. unsigned char* blockBuffer = this->ReadBlock(firstBlock); if (!blockBuffer) { return 0; } size_t n = endBlockOffset - beginBlockOffset; memcpy(data, blockBuffer + beginBlockOffset, n); delete[] blockBuffer; // Byte swap this block. Note that n will always be an integer // multiple of the word size. this->PerformByteSwap(data, n / wordSize, wordSize); } else { // Read all the complete blocks first. size_t length = endOffset - beginOffset; unsigned char* outputPointer = data; size_t blockSize = this->FindBlockSize(firstBlock); // Read the first block. unsigned char* blockBuffer = this->ReadBlock(firstBlock); if (!blockBuffer) { return 0; } size_t n = blockSize - beginBlockOffset; memcpy(outputPointer, blockBuffer + beginBlockOffset, n); delete[] blockBuffer; // Byte swap the first block. Note that n will always be an // integer multiple of the word size. this->PerformByteSwap(outputPointer, n / wordSize, wordSize); // Advance the pointer to the beginning of the second block. outputPointer += blockSize - beginBlockOffset; // Report progress. this->UpdateProgress(float(outputPointer - data) / length); unsigned int currentBlock = firstBlock + 1; for (; currentBlock != lastBlock && !this->Abort; ++currentBlock) { // Read this block. if (!this->ReadBlock(currentBlock, outputPointer)) { return 0; } // Byte swap this block. Note that blockSize will always be an // integer multiple of the word size. this->PerformByteSwap(outputPointer, blockSize / wordSize, wordSize); // Advance the pointer to the beginning of the next block. outputPointer += this->FindBlockSize(currentBlock); // Report progress. this->UpdateProgress(float(outputPointer - data) / length); } // Now read the final block, which is incomplete if it exists. if (endBlockOffset > 0 && !this->Abort) { blockBuffer = this->ReadBlock(lastBlock); if (!blockBuffer) { return 0; } memcpy(outputPointer, blockBuffer, endBlockOffset); delete[] blockBuffer; // Byte swap the partial block. Note that endBlockOffset will // always be an integer multiple of the word size. this->PerformByteSwap(outputPointer, endBlockOffset / wordSize, wordSize); } } this->UpdateProgress(1); // Return the total words actually read. return (endOffset - beginOffset) / wordSize; } //------------------------------------------------------------------------------ vtkXMLDataElement* vtkXMLDataParser::GetRootElement() { return this->RootElement; } //------------------------------------------------------------------------------ size_t vtkXMLDataParser::ReadBinaryData( void* in_buffer, vtkTypeUInt64 startWord, size_t numWords, int wordType) { // Skip real read if aborting. if (this->Abort) { return 0; } size_t wordSize = this->GetWordTypeSize(wordType); void* buffer = in_buffer; // Make sure our streams are setup correctly. this->DataStream->SetStream(this->Stream); // Read the data. unsigned char* d = reinterpret_cast(buffer); size_t actualWords; if (this->Compressor) { if (!this->ReadCompressionHeader()) { vtkErrorMacro("ReadCompressionHeader failed. Aborting read."); return 0; } this->DataStream->StartReading(); actualWords = this->ReadCompressedData(d, startWord, numWords, wordSize); this->DataStream->EndReading(); } else { this->DataStream->StartReading(); actualWords = this->ReadUncompressedData(d, startWord, numWords, wordSize); this->DataStream->EndReading(); } // Return the actual amount read. return this->Abort ? 0 : actualWords; } //------------------------------------------------------------------------------ size_t vtkXMLDataParser::ReadAsciiData( void* buffer, vtkTypeUInt64 startWord, size_t numWords, int wordType) { // Skip real read if aborting. if (this->Abort) { return 0; } // We assume that ascii data are not very large and parse the entire // block into memory. this->UpdateProgress(0); // Parse the ascii data from the file. if (!this->ParseAsciiData(wordType)) { return 0; } // Make sure we don't read outside the range of data available. vtkTypeUInt64 endWord = startWord + numWords; if (this->AsciiDataBufferLength < startWord) { return 0; } if (endWord > this->AsciiDataBufferLength) { endWord = this->AsciiDataBufferLength; } size_t wordSize = this->GetWordTypeSize(wordType); size_t actualWords = endWord - startWord; size_t actualBytes = wordSize * actualWords; size_t startByte = wordSize * startWord; this->UpdateProgress(0.5); // Copy the data from the pre-parsed ascii data buffer. if (buffer && actualBytes) { memcpy(buffer, this->AsciiDataBuffer + startByte, actualBytes); } this->UpdateProgress(1); return this->Abort ? 0 : actualWords; } //------------------------------------------------------------------------------ size_t vtkXMLDataParser::ReadInlineData(vtkXMLDataElement* element, int isAscii, void* buffer, vtkTypeUInt64 startWord, size_t numWords, int wordType) { this->DataStream = this->InlineDataStream; this->SeekInlineDataPosition(element); if (isAscii) { return this->ReadAsciiData(buffer, startWord, numWords, wordType); } else { return this->ReadBinaryData(buffer, startWord, numWords, wordType); } } //------------------------------------------------------------------------------ size_t vtkXMLDataParser::ReadAppendedData( vtkTypeInt64 offset, void* buffer, vtkTypeUInt64 startWord, size_t numWords, int wordType) { this->DataStream = this->AppendedDataStream; this->SeekG(this->AppendedDataPosition + offset); return this->ReadBinaryData(buffer, startWord, numWords, wordType); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // Define a parsing function template. The extra "long" argument is used // to help broken compilers select the non-templates below for char and // unsigned char, and float/double by making them a better conversion than // the template. template T* vtkXMLParseAsciiData(istream& is, int* length, T*, long) { int dataLength = 0; int dataBufferSize = 64; T* dataBuffer = new T[dataBufferSize]; T element; while (is >> element) { if (dataLength == dataBufferSize) { int newSize = dataBufferSize * 2; T* newBuffer = new T[newSize]; memcpy(newBuffer, dataBuffer, dataLength * sizeof(T)); delete[] dataBuffer; dataBuffer = newBuffer; dataBufferSize = newSize; } dataBuffer[dataLength++] = element; } if (length) { *length = dataLength; } return dataBuffer; } //------------------------------------------------------------------------------ static float* vtkXMLParseAsciiData(istream& is, int* length, float*, int) { int dataLength = 0; int dataBufferSize = 64; float* dataBuffer = new float[dataBufferSize]; std::string stringBuffer; float element; while (true) { is >> element; if (!is.good()) { is.clear(is.rdstate() & ~ios::failbit); is >> stringBuffer; if (!is.good()) { break; } else { std::for_each( stringBuffer.begin(), stringBuffer.end(), [](char& c) { c = std::tolower(c); }); if (stringBuffer == "inf" || stringBuffer == "nan" || stringBuffer == "-inf") { element = strtof(stringBuffer.c_str(), nullptr); } else { break; } } } if (dataLength == dataBufferSize) { int newSize = dataBufferSize * 2; float* newBuffer = new float[newSize]; memcpy(newBuffer, dataBuffer, dataLength * sizeof(float)); delete[] dataBuffer; dataBuffer = newBuffer; dataBufferSize = newSize; } dataBuffer[dataLength++] = element; } if (length) { *length = dataLength; } return dataBuffer; } //------------------------------------------------------------------------------ static double* vtkXMLParseAsciiData(istream& is, int* length, double*, int) { int dataLength = 0; int dataBufferSize = 64; double* dataBuffer = new double[dataBufferSize]; std::string stringBuffer; double element; while (true) { is >> element; if (!is.good()) { is.clear(is.rdstate() & ~ios::failbit); is >> stringBuffer; if (!is.good()) { break; } else { std::for_each( stringBuffer.begin(), stringBuffer.end(), [](char& c) { c = std::tolower(c); }); if (stringBuffer == "inf" || stringBuffer == "nan" || stringBuffer == "-inf") { element = strtod(stringBuffer.c_str(), nullptr); } else { break; } } } if (dataLength == dataBufferSize) { int newSize = dataBufferSize * 2; double* newBuffer = new double[newSize]; memcpy(newBuffer, dataBuffer, dataLength * sizeof(double)); delete[] dataBuffer; dataBuffer = newBuffer; dataBufferSize = newSize; } dataBuffer[dataLength++] = element; } if (length) { *length = dataLength; } return dataBuffer; } //------------------------------------------------------------------------------ static char* vtkXMLParseAsciiData(istream& is, int* length, char*, int) { int dataLength = 0; int dataBufferSize = 64; char* dataBuffer = new char[dataBufferSize]; char element; short inElement; while (is >> inElement) { element = inElement; if (dataLength == dataBufferSize) { int newSize = dataBufferSize * 2; char* newBuffer = new char[newSize]; memcpy(newBuffer, dataBuffer, dataLength * sizeof(char)); delete[] dataBuffer; dataBuffer = newBuffer; dataBufferSize = newSize; } dataBuffer[dataLength++] = element; } if (length) { *length = dataLength; } return dataBuffer; } //------------------------------------------------------------------------------ static unsigned char* vtkXMLParseAsciiData(istream& is, int* length, unsigned char*, int) { int dataLength = 0; int dataBufferSize = 64; unsigned char* dataBuffer = new unsigned char[dataBufferSize]; unsigned char element; short inElement; while (is >> inElement) { element = inElement; if (dataLength == dataBufferSize) { int newSize = dataBufferSize * 2; unsigned char* newBuffer = new unsigned char[newSize]; memcpy(newBuffer, dataBuffer, dataLength * sizeof(unsigned char)); delete[] dataBuffer; dataBuffer = newBuffer; dataBufferSize = newSize; } dataBuffer[dataLength++] = element; } if (length) { *length = dataLength; } return dataBuffer; } //------------------------------------------------------------------------------ static signed char* vtkXMLParseAsciiData(istream& is, int* length, signed char*, int) { int dataLength = 0; int dataBufferSize = 64; signed char* dataBuffer = new signed char[dataBufferSize]; signed char element; short inElement; while (is >> inElement) { element = inElement; if (dataLength == dataBufferSize) { int newSize = dataBufferSize * 2; signed char* newBuffer = new signed char[newSize]; memcpy(newBuffer, dataBuffer, dataLength * sizeof(signed char)); delete[] dataBuffer; dataBuffer = newBuffer; dataBufferSize = newSize; } dataBuffer[dataLength++] = element; } if (length) { *length = dataLength; } return dataBuffer; } //------------------------------------------------------------------------------ static unsigned char* vtkXMLParseAsciiBitData(istream& is, int* length) { size_t arrayCapacity = 64; // capacity in bytes unsigned char* array = new unsigned char[arrayCapacity]; std::fill(array, array + arrayCapacity, static_cast(0)); size_t fullBytesRead = 0; unsigned char currentBitInByte = 0; unsigned char* currentByte = array; int value; while (is >> value) { // Realloc array buffer if needed: if (fullBytesRead == arrayCapacity) { assert("sanity check" && currentBitInByte == 0); size_t newSize = arrayCapacity * 2; unsigned char* tmp = new unsigned char[newSize]; std::copy(array, array + arrayCapacity, tmp); std::fill(tmp + arrayCapacity, tmp + newSize, static_cast(0)); delete[] array; array = tmp; currentByte = array + fullBytesRead; arrayCapacity = newSize; } // Set the current bit: assert("sanity check" && currentBitInByte < 8); if (value != 0) { // Mimic the storage mechanism used by vtkBitArray *currentByte = *currentByte | (0x80 >> currentBitInByte); } // Update bookkeeping: if (++currentBitInByte == 8) { ++currentByte; ++fullBytesRead; currentBitInByte = 0; } } if (length) { // We fudge the 'word size' to 1 byte for bit arrays (since it's integral) // so return the length in bytes here: *length = static_cast(fullBytesRead + (currentBitInByte != 0 ? 1 : 0)); } return array; } //------------------------------------------------------------------------------ int vtkXMLDataParser::ParseAsciiData(int wordType) { istream& is = *(this->Stream); // Don't re-parse the same ascii data. if (this->AsciiDataPosition == this->TellG()) { return (this->AsciiDataBuffer ? 1 : 0); } // Prepare for new data. this->AsciiDataPosition = this->TellG(); if (this->AsciiDataBuffer) { this->FreeAsciiBuffer(); } int length = 0; void* buffer = nullptr; switch (wordType) { vtkTemplateMacro(buffer = vtkXMLParseAsciiData(is, &length, static_cast(nullptr), 1)); case VTK_BIT: buffer = vtkXMLParseAsciiBitData(is, &length); break; } // Read terminated from failure. Clear the fail bit so another read // can take place later. is.clear(is.rdstate() & ~ios::failbit); // Save the buffer. this->AsciiDataBuffer = reinterpret_cast(buffer); this->AsciiDataBufferLength = length; this->AsciiDataWordType = wordType; return (this->AsciiDataBuffer ? 1 : 0); } //------------------------------------------------------------------------------ template void vtkXMLDataParserFreeAsciiBuffer(T* buffer) { delete[] buffer; } //------------------------------------------------------------------------------ void vtkXMLDataParser::FreeAsciiBuffer() { void* buffer = this->AsciiDataBuffer; switch (this->AsciiDataWordType) { vtkTemplateMacro(vtkXMLDataParserFreeAsciiBuffer(static_cast(buffer))); case VTK_BIT: vtkXMLDataParserFreeAsciiBuffer(static_cast(buffer)); break; } this->AsciiDataBuffer = nullptr; } //------------------------------------------------------------------------------ void vtkXMLDataParser::UpdateProgress(float progress) { this->Progress = progress; double dProgress = progress; this->InvokeEvent(vtkCommand::ProgressEvent, &dProgress); }