nuclear@0: /* nuclear@0: --------------------------------------------------------------------------- nuclear@0: Open Asset Import Library (assimp) nuclear@0: --------------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the following nuclear@0: conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: --------------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** @file Defines the StreamReader class which reads data from nuclear@0: * a binary stream with a well-defined endianess. */ nuclear@0: nuclear@0: #ifndef AI_STREAMREADER_H_INCLUDED nuclear@0: #define AI_STREAMREADER_H_INCLUDED nuclear@0: nuclear@0: #include "ByteSwap.h" nuclear@0: nuclear@0: namespace Assimp { nuclear@0: nuclear@0: // -------------------------------------------------------------------------------------------- nuclear@0: /** Wrapper class around IOStream to allow for consistent reading of binary data in both nuclear@0: * little and big endian format. Don't attempt to instance the template directly. Use nuclear@0: * StreamReaderLE to read from a little-endian stream and StreamReaderBE to read from a nuclear@0: * BE stream. The class expects that the endianess of any input data is known at nuclear@0: * compile-time, which should usually be true (#BaseImporter::ConvertToUTF8 implements nuclear@0: * runtime endianess conversions for text files). nuclear@0: * nuclear@0: * XXX switch from unsigned int for size types to size_t? or ptrdiff_t?*/ nuclear@0: // -------------------------------------------------------------------------------------------- nuclear@0: template nuclear@0: class StreamReader nuclear@0: { nuclear@0: nuclear@0: public: nuclear@0: nuclear@0: // FIXME: use these data types throughout the whole library, nuclear@0: // then change them to 64 bit values :-) nuclear@0: nuclear@0: typedef int diff; nuclear@0: typedef unsigned int pos; nuclear@0: nuclear@0: public: nuclear@0: nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Construction from a given stream with a well-defined endianess. nuclear@0: * nuclear@0: * The StreamReader holds a permanent strong reference to the nuclear@0: * stream, which is released upon destruction. nuclear@0: * @param stream Input stream. The stream is not restarted if nuclear@0: * its file pointer is not at 0. Instead, the stream reader nuclear@0: * reads from the current position to the end of the stream. nuclear@0: * @param le If @c RuntimeSwitch is true: specifies whether the nuclear@0: * stream is in little endian byte order. Otherwise the nuclear@0: * endianess information is contained in the @c SwapEndianess nuclear@0: * template parameter and this parameter is meaningless. */ nuclear@0: StreamReader(boost::shared_ptr stream, bool le = false) nuclear@0: : stream(stream) nuclear@0: , le(le) nuclear@0: { nuclear@0: ai_assert(stream); nuclear@0: InternBegin(); nuclear@0: } nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: StreamReader(IOStream* stream, bool le = false) nuclear@0: : stream(boost::shared_ptr(stream)) nuclear@0: , le(le) nuclear@0: { nuclear@0: ai_assert(stream); nuclear@0: InternBegin(); nuclear@0: } nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: ~StreamReader() { nuclear@0: delete[] buffer; nuclear@0: } nuclear@0: nuclear@0: public: nuclear@0: nuclear@0: // deprecated, use overloaded operator>> instead nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Read a float from the stream */ nuclear@0: float GetF4() nuclear@0: { nuclear@0: return Get(); nuclear@0: } nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Read a double from the stream */ nuclear@0: double GetF8() { nuclear@0: return Get(); nuclear@0: } nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Read a signed 16 bit integer from the stream */ nuclear@0: int16_t GetI2() { nuclear@0: return Get(); nuclear@0: } nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Read a signed 8 bit integer from the stream */ nuclear@0: int8_t GetI1() { nuclear@0: return Get(); nuclear@0: } nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Read an signed 32 bit integer from the stream */ nuclear@0: int32_t GetI4() { nuclear@0: return Get(); nuclear@0: } nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Read a signed 64 bit integer from the stream */ nuclear@0: int64_t GetI8() { nuclear@0: return Get(); nuclear@0: } nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Read a unsigned 16 bit integer from the stream */ nuclear@0: uint16_t GetU2() { nuclear@0: return Get(); nuclear@0: } nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Read a unsigned 8 bit integer from the stream */ nuclear@0: uint8_t GetU1() { nuclear@0: return Get(); nuclear@0: } nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Read an unsigned 32 bit integer from the stream */ nuclear@0: uint32_t GetU4() { nuclear@0: return Get(); nuclear@0: } nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Read a unsigned 64 bit integer from the stream */ nuclear@0: uint64_t GetU8() { nuclear@0: return Get(); nuclear@0: } nuclear@0: nuclear@0: public: nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Get the remaining stream size (to the end of the srream) */ nuclear@0: unsigned int GetRemainingSize() const { nuclear@0: return (unsigned int)(end - current); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Get the remaining stream size (to the current read limit). The nuclear@0: * return value is the remaining size of the stream if no custom nuclear@0: * read limit has been set. */ nuclear@0: unsigned int GetRemainingSizeToLimit() const { nuclear@0: return (unsigned int)(limit - current); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Increase the file pointer (relative seeking) */ nuclear@0: void IncPtr(int plus) { nuclear@0: current += plus; nuclear@0: if (current > limit) { nuclear@0: throw DeadlyImportError("End of file or read limit was reached"); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Get the current file pointer */ nuclear@0: int8_t* GetPtr() const { nuclear@0: return current; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Set current file pointer (Get it from #GetPtr). This is if you nuclear@0: * prefer to do pointer arithmetics on your own or want to copy nuclear@0: * large chunks of data at once. nuclear@0: * @param p The new pointer, which is validated against the size nuclear@0: * limit and buffer boundaries. */ nuclear@0: void SetPtr(int8_t* p) { nuclear@0: nuclear@0: current = p; nuclear@0: if (current > limit || current < buffer) { nuclear@0: throw DeadlyImportError("End of file or read limit was reached"); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Copy n bytes to an external buffer nuclear@0: * @param out Destination for copying nuclear@0: * @param bytes Number of bytes to copy */ nuclear@0: void CopyAndAdvance(void* out, size_t bytes) { nuclear@0: nuclear@0: int8_t* ur = GetPtr(); nuclear@0: SetPtr(ur+bytes); // fire exception if eof nuclear@0: nuclear@0: memcpy(out,ur,bytes); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Get the current offset from the beginning of the file */ nuclear@0: int GetCurrentPos() const { nuclear@0: return (unsigned int)(current - buffer); nuclear@0: } nuclear@0: nuclear@0: void SetCurrentPos(size_t pos) { nuclear@0: SetPtr(buffer + pos); nuclear@0: } nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Setup a temporary read limit nuclear@0: * nuclear@0: * @param limit Maximum number of bytes to be read from nuclear@0: * the beginning of the file. Specifying UINT_MAX nuclear@0: * resets the limit to the original end of the stream. */ nuclear@0: void SetReadLimit(unsigned int _limit) { nuclear@0: nuclear@0: if (UINT_MAX == _limit) { nuclear@0: limit = end; nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: limit = buffer + _limit; nuclear@0: if (limit > end) { nuclear@0: throw DeadlyImportError("StreamReader: Invalid read limit"); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Get the current read limit in bytes. Reading over this limit nuclear@0: * accidentially raises an exception. */ nuclear@0: int GetReadLimit() const { nuclear@0: return (unsigned int)(limit - buffer); nuclear@0: } nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Skip to the read limit in bytes. Reading over this limit nuclear@0: * accidentially raises an exception. */ nuclear@0: void SkipToReadLimit() { nuclear@0: current = limit; nuclear@0: } nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** overload operator>> and allow chaining of >> ops. */ nuclear@0: template nuclear@0: StreamReader& operator >> (T& f) { nuclear@0: f = Get(); nuclear@0: return *this; nuclear@0: } nuclear@0: nuclear@0: private: nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: /** Generic read method. ByteSwap::Swap(T*) *must* be defined */ nuclear@0: template nuclear@0: T Get() { nuclear@0: if (current + sizeof(T) > limit) { nuclear@0: throw DeadlyImportError("End of file or stream limit was reached"); nuclear@0: } nuclear@0: nuclear@0: #ifdef __arm__ nuclear@0: T f; nuclear@0: memcpy (&f, current, sizeof(T)); nuclear@0: #else nuclear@0: T f = *((const T*)current); nuclear@0: #endif nuclear@0: Intern :: Getter() (&f,le); nuclear@0: nuclear@0: current += sizeof(T); nuclear@0: return f; nuclear@0: } nuclear@0: nuclear@0: // --------------------------------------------------------------------- nuclear@0: void InternBegin() { nuclear@0: if (!stream) { nuclear@0: // incase someone wonders: StreamReader is frequently invoked with nuclear@0: // no prior validation whether the input stream is valid. Since nuclear@0: // no one bothers changing the error message, this message here nuclear@0: // is passed down to the caller and 'unable to open file' nuclear@0: // simply describes best what happened. nuclear@0: throw DeadlyImportError("StreamReader: Unable to open file"); nuclear@0: } nuclear@0: nuclear@0: const size_t s = stream->FileSize() - stream->Tell(); nuclear@0: if (!s) { nuclear@0: throw DeadlyImportError("StreamReader: File is empty or EOF is already reached"); nuclear@0: } nuclear@0: nuclear@0: current = buffer = new int8_t[s]; nuclear@0: const size_t read = stream->Read(current,1,s); nuclear@0: // (read < s) can only happen if the stream was opened in text mode, in which case FileSize() is not reliable nuclear@0: ai_assert(read <= s); nuclear@0: end = limit = &buffer[read]; nuclear@0: } nuclear@0: nuclear@0: private: nuclear@0: nuclear@0: nuclear@0: boost::shared_ptr stream; nuclear@0: int8_t *buffer, *current, *end, *limit; nuclear@0: bool le; nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: // -------------------------------------------------------------------------------------------- nuclear@0: // `static` StreamReaders. Their byte order is fixed and they might be a little bit faster. nuclear@0: #ifdef AI_BUILD_BIG_ENDIAN nuclear@0: typedef StreamReader StreamReaderLE; nuclear@0: typedef StreamReader StreamReaderBE; nuclear@0: #else nuclear@0: typedef StreamReader StreamReaderBE; nuclear@0: typedef StreamReader StreamReaderLE; nuclear@0: #endif nuclear@0: nuclear@0: // `dynamic` StreamReader. The byte order of the input data is specified in the nuclear@0: // c'tor. This involves runtime branching and might be a little bit slower. nuclear@0: typedef StreamReader StreamReaderAny; nuclear@0: nuclear@0: } // end namespace Assimp nuclear@0: nuclear@0: #endif // !! AI_STREAMREADER_H_INCLUDED