You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1059 lines
31 KiB
1059 lines
31 KiB
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: $RCSfile: vtkXMLDataParser.cxx,v $
|
|
|
|
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 "vtkInputStream.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkXMLDataElement.h"
|
|
|
|
vtkCxxRevisionMacro(vtkXMLDataParser, "$Revision: 1.29.6.1 $");
|
|
vtkStandardNewMacro(vtkXMLDataParser);
|
|
vtkCxxSetObjectMacro(vtkXMLDataParser, Compressor, vtkDataCompressor);
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkXMLDataParser::vtkXMLDataParser()
|
|
{
|
|
this->NumberOfOpenElements = 0;
|
|
this->OpenElementsSize = 10;
|
|
this->OpenElements = new vtkXMLDataElement*[this->OpenElementsSize];
|
|
this->RootElement = 0;
|
|
this->AppendedDataPosition = 0;
|
|
this->AppendedDataMatched = 0;
|
|
this->DataStream = 0;
|
|
this->InlineDataStream = vtkBase64InputStream::New();
|
|
this->AppendedDataStream = vtkBase64InputStream::New();
|
|
|
|
this->BlockCompressedSizes = 0;
|
|
this->BlockStartOffsets = 0;
|
|
this->Compressor = 0;
|
|
|
|
this->AsciiDataBuffer = 0;
|
|
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->AttributesEncoding = VTK_ENCODING_NONE;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkXMLDataParser::~vtkXMLDataParser()
|
|
{
|
|
this->FreeAllElements();
|
|
delete [] this->OpenElements;
|
|
this->InlineDataStream->Delete();
|
|
this->AppendedDataStream->Delete();
|
|
if(this->BlockCompressedSizes) { delete [] this->BlockCompressedSizes; }
|
|
if(this->BlockStartOffsets) { delete [] this->BlockStartOffsets; }
|
|
this->SetCompressor(0);
|
|
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());
|
|
element->ReadXMLAttributes(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::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;
|
|
}
|
|
}
|
|
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;
|
|
long returnPosition = this->TellG();
|
|
this->SeekG(this->GetXMLByteIndex());
|
|
while(this->Stream->get(c) && (c != '>'));
|
|
while(this->Stream->get(c) && this->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);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkXMLDataParser::OffsetType
|
|
vtkXMLDataParser::FindInlineDataPosition(OffsetType start)
|
|
{
|
|
// Scan for the start of the actual inline data.
|
|
char c=0;
|
|
this->SeekG(start);
|
|
while(this->Stream->get(c) && (c != '>'));
|
|
while(this->Stream->get(c) && this->IsSpace(c));
|
|
|
|
// Make sure some data were found.
|
|
if(c == '<') { return 0; }
|
|
OffsetType pos = this->TellG();
|
|
return (pos-1);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
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 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLDataParser::FreeAllElements()
|
|
{
|
|
while(this->NumberOfOpenElements > 0)
|
|
{
|
|
--this->NumberOfOpenElements;
|
|
this->OpenElements[this->NumberOfOpenElements]->Delete();
|
|
this->OpenElements[this->NumberOfOpenElements] = 0;
|
|
}
|
|
if(this->RootElement)
|
|
{
|
|
this->RootElement->Delete();
|
|
this->RootElement = 0;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkXMLDataParser::ParseBuffer(const char* buffer, unsigned int count)
|
|
{
|
|
// Parsing must stop when "<AppendedData" is reached. Use a search
|
|
// similar to the KMP string search algorithm.
|
|
const char pattern[] = "<AppendedData";
|
|
const int length = sizeof(pattern)-1;
|
|
|
|
const char* s = buffer;
|
|
const char* end = buffer + count;
|
|
int matched = this->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</VTKFile>\n";
|
|
if(!this->Superclass::ParseBuffer(finish, sizeof(finish)-1)) { return 0; }
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
template <class T>
|
|
unsigned long vtkXMLDataParserGetWordTypeSize(T*)
|
|
{
|
|
return sizeof(T);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
unsigned long vtkXMLDataParser::GetWordTypeSize(int wordType)
|
|
{
|
|
unsigned long size = 1;
|
|
switch (wordType)
|
|
{
|
|
vtkTemplateMacro(
|
|
size = vtkXMLDataParserGetWordTypeSize(static_cast<VTK_TT*>(0))
|
|
);
|
|
default:
|
|
{ vtkWarningMacro("Unsupported data type: " << wordType); } break;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLDataParser::PerformByteSwap(void* data, OffsetType numWords,
|
|
int wordSize)
|
|
{
|
|
char* ptr = static_cast<char*>(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);
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLDataParser::ReadCompressionHeader()
|
|
{
|
|
HeaderType headerBuffer[3];
|
|
const int headerSize = sizeof(headerBuffer);
|
|
unsigned char* buffer = reinterpret_cast<unsigned char*>(&headerBuffer[0]);
|
|
|
|
this->DataStream->StartReading();
|
|
|
|
// Read the standard part of the header.
|
|
int r = this->DataStream->Read(buffer, headerSize);
|
|
if(r < headerSize)
|
|
{
|
|
vtkErrorMacro("Error reading beginning of compression header. Read "
|
|
<< r << " of " << headerSize << " bytes.");
|
|
return;
|
|
}
|
|
|
|
// Byte swap the header to make sure the values are correct.
|
|
this->PerformByteSwap(headerBuffer, 3, sizeof(HeaderType));
|
|
|
|
// Get the standard values.
|
|
this->NumberOfBlocks = headerBuffer[0];
|
|
this->BlockUncompressedSize = headerBuffer[1];
|
|
this->PartialLastBlockUncompressedSize = headerBuffer[2];
|
|
|
|
// Allocate the size and offset parts of the header.
|
|
if(this->BlockCompressedSizes)
|
|
{
|
|
delete [] this->BlockCompressedSizes;
|
|
this->BlockCompressedSizes = 0;
|
|
}
|
|
if(this->BlockStartOffsets)
|
|
{
|
|
delete [] this->BlockStartOffsets;
|
|
this->BlockStartOffsets = 0;
|
|
}
|
|
if(this->NumberOfBlocks > 0)
|
|
{
|
|
this->BlockCompressedSizes = new HeaderType[this->NumberOfBlocks];
|
|
this->BlockStartOffsets = new OffsetType[this->NumberOfBlocks];
|
|
|
|
buffer = reinterpret_cast<unsigned char*>(&this->BlockCompressedSizes[0]);
|
|
|
|
// Read the compressed block sizes.
|
|
unsigned long len = this->NumberOfBlocks*sizeof(HeaderType);
|
|
if(this->DataStream->Read(buffer, len) < len)
|
|
{
|
|
vtkErrorMacro("Error reading compression header.");
|
|
return;
|
|
}
|
|
|
|
// Byte swap the sizes to make sure the values are correct.
|
|
this->PerformByteSwap(buffer, this->NumberOfBlocks, sizeof(HeaderType));
|
|
}
|
|
|
|
this->DataStream->EndReading();
|
|
|
|
// Use the compressed block sizes to calculate the starting offset
|
|
// of each block.
|
|
OffsetType offset = 0;
|
|
unsigned int i;
|
|
for(i=0;i < this->NumberOfBlocks;++i)
|
|
{
|
|
this->BlockStartOffsets[i] = offset;
|
|
offset += this->BlockCompressedSizes[i];
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
unsigned int vtkXMLDataParser::FindBlockSize(unsigned int block)
|
|
{
|
|
if(block < this->NumberOfBlocks-(this->PartialLastBlockUncompressedSize?1:0))
|
|
{
|
|
return this->BlockUncompressedSize;
|
|
}
|
|
else
|
|
{
|
|
return this->PartialLastBlockUncompressedSize;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkXMLDataParser::ReadBlock(unsigned int block, unsigned char* buffer)
|
|
{
|
|
OffsetType uncompressedSize = this->FindBlockSize(block);
|
|
unsigned int compressedSize = this->BlockCompressedSizes[block];
|
|
unsigned char* readBuffer = new unsigned char[compressedSize];
|
|
|
|
if(!this->DataStream->Seek(this->BlockStartOffsets[block]))
|
|
{
|
|
return 0;
|
|
}
|
|
if(this->DataStream->Read(readBuffer, compressedSize) < compressedSize)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
OffsetType result =
|
|
this->Compressor->Uncompress(readBuffer, compressedSize,
|
|
buffer, uncompressedSize);
|
|
|
|
delete [] readBuffer;
|
|
return (result > 0)? 1:0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
unsigned char* vtkXMLDataParser::ReadBlock(unsigned int block)
|
|
{
|
|
unsigned char* decompressBuffer =
|
|
new unsigned char[this->FindBlockSize(block)];
|
|
if(!this->ReadBlock(block, decompressBuffer))
|
|
{
|
|
delete [] decompressBuffer;
|
|
return 0;
|
|
}
|
|
return decompressBuffer;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkXMLDataParser::OffsetType
|
|
vtkXMLDataParser::ReadUncompressedData(unsigned char* data,
|
|
OffsetType startWord,
|
|
OffsetType numWords,
|
|
int wordSize)
|
|
{
|
|
// First read the length of the data.
|
|
HeaderType rsize;
|
|
const unsigned long len = sizeof(HeaderType);
|
|
unsigned char* p = reinterpret_cast<unsigned char*>(&rsize);
|
|
if(this->DataStream->Read(p, len) < len) { return 0; }
|
|
this->PerformByteSwap(&rsize, 1, len);
|
|
|
|
// 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.
|
|
OffsetType size = (rsize/wordSize)*wordSize;
|
|
|
|
// Convert the start/length into bytes.
|
|
OffsetType offset = startWord*wordSize;
|
|
OffsetType length = numWords*wordSize;
|
|
|
|
// Make sure the begin/end offsets fall within total size.
|
|
if(offset > size)
|
|
{
|
|
return 0;
|
|
}
|
|
OffsetType end = offset+length;
|
|
if(end > size)
|
|
{
|
|
end = size;
|
|
}
|
|
length = end-offset;
|
|
|
|
// Read the data.
|
|
if(!this->DataStream->Seek(offset+len))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Read data in 32KB blocks and report progress.
|
|
const long blockSize = 32768;
|
|
long left = length;
|
|
p = data;
|
|
this->UpdateProgress(0);
|
|
while(left > 0 && !this->Abort)
|
|
{
|
|
// Read this block.
|
|
long 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;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkXMLDataParser::OffsetType
|
|
vtkXMLDataParser::ReadCompressedData(unsigned char* data,
|
|
OffsetType startWord,
|
|
OffsetType numWords,
|
|
int wordSize)
|
|
{
|
|
// Make sure there are data.
|
|
if(numWords == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Find the begin and end offsets into the data.
|
|
OffsetType beginOffset = startWord*wordSize;
|
|
OffsetType endOffset = beginOffset+numWords*wordSize;
|
|
|
|
// Find the total size of the data.
|
|
OffsetType totalSize = this->NumberOfBlocks*this->BlockUncompressedSize;
|
|
if(this->PartialLastBlockUncompressedSize)
|
|
{
|
|
totalSize -= this->BlockUncompressedSize;
|
|
totalSize += this->PartialLastBlockUncompressedSize;
|
|
}
|
|
|
|
// 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.
|
|
unsigned int firstBlock = beginOffset / this->BlockUncompressedSize;
|
|
unsigned int lastBlock = endOffset / this->BlockUncompressedSize;
|
|
|
|
// Find the offset into the first block where the data begin.
|
|
unsigned int beginBlockOffset =
|
|
beginOffset - firstBlock*this->BlockUncompressedSize;
|
|
|
|
// Find the offset into the last block where the data end.
|
|
unsigned int 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; }
|
|
long 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.
|
|
OffsetType length = endOffset - beginOffset;
|
|
unsigned char* outputPointer = data;
|
|
OffsetType blockSize = this->FindBlockSize(firstBlock);
|
|
|
|
// Read the first block.
|
|
unsigned char* blockBuffer = this->ReadBlock(firstBlock);
|
|
if(!blockBuffer)
|
|
{
|
|
return 0;
|
|
}
|
|
long 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;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkXMLDataParser::OffsetType
|
|
vtkXMLDataParser::ReadBinaryData(void* in_buffer,
|
|
OffsetType startWord,
|
|
OffsetType numWords,
|
|
int wordType)
|
|
{
|
|
// Skip real read if aborting.
|
|
if(this->Abort)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
unsigned long 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<unsigned char*>(buffer);
|
|
OffsetType actualWords;
|
|
if(this->Compressor)
|
|
{
|
|
this->ReadCompressionHeader();
|
|
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;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkXMLDataParser::OffsetType
|
|
vtkXMLDataParser::ReadAsciiData(void* buffer,
|
|
OffsetType startWord,
|
|
OffsetType 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.
|
|
OffsetType endWord = startWord + numWords;
|
|
if(this->AsciiDataBufferLength < startWord) { return 0; }
|
|
if(endWord > this->AsciiDataBufferLength)
|
|
{
|
|
endWord = this->AsciiDataBufferLength;
|
|
}
|
|
unsigned long wordSize = this->GetWordTypeSize(wordType);
|
|
OffsetType actualWords = endWord - startWord;
|
|
OffsetType actualBytes = wordSize*actualWords;
|
|
OffsetType startByte = wordSize*startWord;
|
|
|
|
this->UpdateProgress(0.5);
|
|
|
|
// Copy the data from the pre-parsed ascii data buffer.
|
|
memcpy(buffer, this->AsciiDataBuffer+startByte, actualBytes);
|
|
|
|
this->UpdateProgress(1);
|
|
|
|
return this->Abort? 0:actualWords;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkXMLDataParser::OffsetType
|
|
vtkXMLDataParser::ReadInlineData(vtkXMLDataElement* element,
|
|
int isAscii, void* buffer,
|
|
OffsetType startWord,
|
|
OffsetType numWords,
|
|
int wordType)
|
|
{
|
|
this->DataStream = this->InlineDataStream;
|
|
element->SeekInlineDataPosition(this);
|
|
if(isAscii)
|
|
{
|
|
return this->ReadAsciiData(buffer, startWord, numWords, wordType);
|
|
}
|
|
else
|
|
{
|
|
return this->ReadBinaryData(buffer, startWord, numWords, wordType);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkXMLDataParser::OffsetType
|
|
vtkXMLDataParser::ReadAppendedData(OffsetType offset,
|
|
void* buffer,
|
|
OffsetType startWord,
|
|
OffsetType 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 by making them a better conversion than the template.
|
|
template <class T>
|
|
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;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
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;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
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;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
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;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkXMLDataParser::ParseAsciiData(int wordType)
|
|
{
|
|
istream& is = *(this->Stream);
|
|
|
|
// Don't re-parse the same ascii data.
|
|
if(this->AsciiDataPosition == static_cast<OffsetType>(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 = 0;
|
|
switch (wordType)
|
|
{
|
|
vtkTemplateMacro(
|
|
buffer = vtkXMLParseAsciiData(is, &length, static_cast<VTK_TT*>(0), 1)
|
|
);
|
|
}
|
|
|
|
// 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<unsigned char*>(buffer);
|
|
this->AsciiDataBufferLength = length;
|
|
this->AsciiDataWordType = wordType;
|
|
return (this->AsciiDataBuffer? 1:0);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
template <class T>
|
|
void vtkXMLDataParserFreeAsciiBuffer(T* buffer)
|
|
{
|
|
delete [] buffer;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLDataParser::FreeAsciiBuffer()
|
|
{
|
|
void* buffer = this->AsciiDataBuffer;
|
|
switch (this->AsciiDataWordType)
|
|
{
|
|
vtkTemplateMacro(
|
|
vtkXMLDataParserFreeAsciiBuffer(static_cast<VTK_TT*>(buffer))
|
|
);
|
|
}
|
|
this->AsciiDataBuffer = 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLDataParser::UpdateProgress(float progress)
|
|
{
|
|
this->Progress = progress;
|
|
this->InvokeEvent(vtkCommand::ProgressEvent, &progress);
|
|
}
|
|
|