See More

/* \file * $Id$ * \author Caton Little * \brief * * \section LICENSE * * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * The Original Code is FieldML * * The Initial Developer of the Original Code is Auckland Uniservices Ltd, * Auckland, New Zealand. Portions created by the Initial Developer are * Copyright (C) 2010 the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * */ #include #include #include "StringUtil.h" #include "FieldmlIoApi.h" #include "TextArrayDataReader.h" #include "InputStream.h" using namespace std; /** * A pseudo-lambda class that removes the need to duplicate the slab and slice reading implementations. * No point in making this a template class, as we need to use a different method on stream depending on the type, * and there's no superclass functionality, so template specialization is redundant. */ class BufferReader { protected: int bufferPos; FieldmlInputStream * const stream; public: BufferReader( FieldmlInputStream *_stream ) : stream( _stream ), bufferPos( 0 ) {} virtual ~BufferReader() {} virtual void read( int count ) = 0; }; class DoubleBufferReader : public BufferReader { private: double * const buffer; public: DoubleBufferReader( FieldmlInputStream *_stream, double *_buffer ) : BufferReader( _stream ), buffer( _buffer ) {} void read( int count ) { for( int i = 0; i < count; i++ ) { buffer[bufferPos++] = stream->readDouble(); } } }; class IntBufferReader : public BufferReader { private: int * const buffer; public: IntBufferReader( FieldmlInputStream *_stream, int *_buffer ) : BufferReader( _stream ), buffer( _buffer ) {} void read( int count ) { for( int i = 0; i < count; i++ ) { buffer[bufferPos++] = stream->readInt(); } } }; class BooleanBufferReader : public BufferReader { private: FmlBoolean * const buffer; public: BooleanBufferReader( FieldmlInputStream *_stream, FmlBoolean *_buffer ) : BufferReader( _stream ), buffer( _buffer ) {} void read( int count ) { for( int i = 0; i < count; i++ ) { buffer[bufferPos++] = stream->readBoolean(); } } }; TextArrayDataReader *TextArrayDataReader::create( FieldmlIoContext *context, const string root, FmlObjectHandle source, void *buffer) { FieldmlInputStream *stream = NULL; FmlObjectHandle resource = Fieldml_GetDataSourceResource( context->getSession(), source ); string format; char *temp_string = Fieldml_GetDataResourceFormat( context->getSession(), resource ); if( !StringUtil::safeString( temp_string, format ) ) { context->setError( FML_IOERR_CORE_ERROR ); return NULL; } Fieldml_FreeString(temp_string); FieldmlDataResourceType type = Fieldml_GetDataResourceType( context->getSession(), resource ); int rank = Fieldml_GetArrayDataSourceRank( context->getSession(), source ); if( rank <= 0 ) { context->setError( FML_IOERR_CORE_ERROR ); return NULL; } if( format != StringUtil::PLAIN_TEXT_NAME ) { context->setError( FML_IOERR_UNSUPPORTED ); return NULL; } if (buffer == 0) { if( type == FML_DATA_RESOURCE_HREF ) { string href; char *temp_href = Fieldml_GetDataResourceHref( context->getSession(), resource ); if( !StringUtil::safeString( temp_href, href ) ) { context->setError( FML_IOERR_CORE_ERROR ); return NULL; } Fieldml_FreeString(temp_href); stream = FieldmlInputStream::createTextFileStream( StringUtil::makeFilename( root, href ) ); } else if( type == FML_DATA_RESOURCE_INLINE ) { string data; char *temp_inline_data = Fieldml_GetInlineData( context->getSession(), resource ); if( !StringUtil::safeString( temp_inline_data, data ) ) { return NULL; } Fieldml_FreeString(temp_inline_data); stream = FieldmlInputStream::createStringStream( data ); } } else { string data; char *temp_data = (char *)buffer; if( !StringUtil::safeString( temp_data, data ) ) { return NULL; } stream = FieldmlInputStream::createStringStream( data ); } if( stream == NULL ) { return NULL; } return new TextArrayDataReader( context, stream, source, rank ); } TextArrayDataReader::TextArrayDataReader( FieldmlIoContext *_context, FieldmlInputStream *_stream, FmlObjectHandle _source, int rank ) : ArrayDataReader( _context ), stream( _stream ), source( _source ), sourceRank( rank ), sourceSizes( NULL ), sourceOffsets( NULL ), sourceRawSizes( NULL ), closed( false ) { startPos = -1; nextOutermostOffset = -1; sourceSizes = new int[sourceRank]; sourceRawSizes = new int[sourceRank]; sourceOffsets = new int[sourceRank]; Fieldml_GetArrayDataSourceSizes( context->getSession(), source, sourceSizes ); Fieldml_GetArrayDataSourceRawSizes( context->getSession(), source, sourceRawSizes ); Fieldml_GetArrayDataSourceOffsets( context->getSession(), source, sourceOffsets ); char *temp_string = Fieldml_GetArrayDataSourceLocation( context->getSession(), source ); StringUtil::safeString( temp_string, sourceLocation ); Fieldml_FreeString(temp_string); } bool TextArrayDataReader::checkDimensions( const int *offsets, const int *sizes ) { for( int i = 0; i < sourceRank; i++ ) { if( offsets[i] < 0 ) { return false; } if( sizes[i] <= 0 ) { return false; } int rawSize = sourceSizes[i]; if( rawSize == 0 ) { //NOTE: Intentional. If the array-source size has not been set, use the underlying size. rawSize = sourceRawSizes[i] - sourceOffsets[i]; } if( offsets[i] + sizes[i] > rawSize ) { return false; } } return true; } FmlIoErrorNumber TextArrayDataReader::skipPreamble() { std::istringstream sstr( sourceLocation ); int lineNumber; if( ! ( sstr >> lineNumber ) ) { return FML_IOERR_INVALID_LOCATION; } for( int i = 1; i < lineNumber; i++ ) { stream->skipLine(); } if( stream->eof() ) { return context->setError( FML_IOERR_UNEXPECTED_EOF ); } startPos = stream->tell(); return FML_IOERR_NO_ERROR; } bool TextArrayDataReader::applyOffsets( const int *offsets, const int *sizes, int depth, bool isHead ) { long count = 1; for( int i = depth+1; i < sourceRank; i++ ) { //NOTE This could overflow in the event that someone puts that much data into a text file. Probability: Lilliputian. count *= sourceRawSizes[i]; } int sliceCount; if( isHead ) { sliceCount = sourceOffsets[depth] + offsets[depth]; if( ( depth == 0 ) && ( nextOutermostOffset >= 0 ) && ( sliceCount >= nextOutermostOffset ) ) { sliceCount -= nextOutermostOffset; } } else { sliceCount = sourceRawSizes[depth] - ( sourceOffsets[depth] + offsets[depth] + sizes[depth] ); } if( sliceCount == 0 ) { return true; } for( int j = 0; j < sliceCount; j++ ) { for( int i = 0; i < count; i++ ) { stream->readDouble(); } } return !stream->eof(); } FmlIoErrorNumber TextArrayDataReader::readPreSlab( const int *offsets, const int *sizes ) { if( !checkDimensions( offsets, sizes ) ) { return context->setError( FML_IOERR_INVALID_PARAMETER ); } if( ( nextOutermostOffset >= 0 ) && ( sourceOffsets[0] + offsets[0] >= nextOutermostOffset ) ) { return FML_IOERR_NO_ERROR; } if( startPos == -1 ) { int err = skipPreamble(); if( err != FML_IOERR_NO_ERROR ) { return err; } } else { stream->seek( startPos ); } return FML_IOERR_NO_ERROR; } FmlIoErrorNumber TextArrayDataReader::readSlice( const int *offsets, const int *sizes, int depth, BufferReader &reader ) { if( !applyOffsets( offsets, sizes, depth, true ) ) { return context->setError( FML_IOERR_UNEXPECTED_EOF ); } if( depth == sourceRank - 1 ) { reader.read( sizes[depth] ); if( stream->eof() ) { return context->setError( FML_IOERR_UNEXPECTED_EOF ); } } else { int err; for( int i = 0; i < sizes[depth]; i++ ) { err = readSlice( offsets, sizes, depth + 1, reader ); if( err != FML_IOERR_NO_ERROR ) { return err; } } } if( depth == 0 ) { nextOutermostOffset = sourceOffsets[0] + offsets[0] + sizes[0]; } else if( !applyOffsets( offsets, sizes, depth, false ) ) { return context->setError( FML_IOERR_UNEXPECTED_EOF ); } return FML_IOERR_NO_ERROR; } FmlIoErrorNumber TextArrayDataReader::readSlab( const int *offsets, const int *sizes, BufferReader &reader ) { int err = readPreSlab( offsets, sizes ); if( err != FML_IOERR_NO_ERROR ) { return err; } return readSlice( offsets, sizes, 0, reader ); } FmlIoErrorNumber TextArrayDataReader::readIntSlab( const int *offsets, const int *sizes, int *valueBuffer ) { if( closed ) { return FML_IOERR_RESOURCE_CLOSED; } IntBufferReader reader( stream, valueBuffer ); return readSlab( offsets, sizes, reader ); } FmlIoErrorNumber TextArrayDataReader::readDoubleSlab( const int *offsets, const int *sizes, double *valueBuffer ) { if( closed ) { return FML_IOERR_RESOURCE_CLOSED; } DoubleBufferReader reader( stream, valueBuffer ); return readSlab( offsets, sizes, reader ); } FmlIoErrorNumber TextArrayDataReader::readBooleanSlab( const int *offsets, const int *sizes, FmlBoolean *valueBuffer ) { if( closed ) { return FML_IOERR_RESOURCE_CLOSED; } BooleanBufferReader reader( stream, valueBuffer ); return readSlab( offsets, sizes, reader ); } FmlIoErrorNumber TextArrayDataReader::close() { if( closed ) { return FML_IOERR_NO_ERROR; } closed = true; return FML_IOERR_NO_ERROR; } TextArrayDataReader::~TextArrayDataReader() { delete stream; delete sourceRawSizes; delete sourceSizes; delete sourceOffsets; }