Explorar o código

Merge pull request #1043 from assimp/new_obj_stream_handling

New obj stream handling
Kim Kulling %!s(int64=9) %!d(string=hai) anos
pai
achega
8b2f1085c7

+ 0 - 1
code/BlenderLoader.h

@@ -105,7 +105,6 @@ public:
     BlenderImporter();
     ~BlenderImporter();
 
-
 public:
 
     // --------------------

+ 1 - 0
code/CMakeLists.txt

@@ -172,6 +172,7 @@ SET( Common_SRCS
   Bitmap.h
   XMLTools.h
   Version.cpp
+  IOStreamBuffer.h
 )
 SOURCE_GROUP(Common FILES ${Common_SRCS})
 

+ 1 - 3
code/DefaultIOStream.h

@@ -105,7 +105,7 @@ public:
     void Flush();
 
 private:
-    //  File datastructure, using clib
+    //  File data-structure, using clib
     FILE* mFile;
     //  Filename
     std::string mFilename;
@@ -114,7 +114,6 @@ private:
     mutable size_t mCachedSize;
 };
 
-
 // ----------------------------------------------------------------------------------
 inline DefaultIOStream::DefaultIOStream () :
     mFile       (NULL),
@@ -124,7 +123,6 @@ inline DefaultIOStream::DefaultIOStream () :
     // empty
 }
 
-
 // ----------------------------------------------------------------------------------
 inline DefaultIOStream::DefaultIOStream (FILE* pFile,
         const std::string &strFilename) :

+ 256 - 0
code/IOStreamBuffer.h

@@ -0,0 +1,256 @@
+#pragma once
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2016, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#include <assimp/types.h>
+#include <assimp/IOStream.hpp>
+#include "ParsingUtils.h"
+
+#include <iostream>
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/**
+ *  Implementation of a cached stream buffer.
+ */
+template<class T>
+class IOStreamBuffer {
+public:
+    /// @brief  The class constructor.
+    IOStreamBuffer( size_t cache = 4096 * 4096 );
+
+    /// @brief  The class destructor.
+    ~IOStreamBuffer();
+
+    /// @brief  Will open the cached access for a given stream.
+    /// @param  stream      The stream to cache.
+    /// @return true if successful.
+    bool open( IOStream *stream );
+
+    /// @brief  Will close the cached access.
+    /// @return true if successful.
+    bool close();
+
+    /// @brief  Returns the filesize.
+    /// @return The filesize.
+    size_t size() const;
+    
+    /// @brief  Returns the cache size.
+    /// @return The cache size.
+    size_t cacheSize() const;
+
+    /// @brief  Will read the next block.
+    /// @return true if successful.
+    bool readNextBlock();
+
+    /// @brief  Returns the number of blocks to read.
+    /// @return The number of blocks.
+    size_t getNumBlocks() const;
+
+    /// @brief  Returns the current block index.
+    /// @return The current block index.
+    size_t getCurrentBlockIndex() const;
+
+    /// @brief  Returns the current file pos.
+    /// @return The current file pos.
+    size_t getFilePos() const;
+
+    /// @brief  Will read the next line.
+    /// @param  buffer      The buffer for the next line.
+    /// @return true if successful.
+    bool getNextLine( std::vector<T> &buffer );
+
+private:
+    IOStream *m_stream;
+    size_t m_filesize;
+    size_t m_cacheSize;
+    size_t m_numBlocks;
+    size_t m_blockIdx;
+    std::vector<T> m_cache;
+    size_t m_cachePos;
+    size_t m_filePos;
+};
+
+template<class T>
+inline
+IOStreamBuffer<T>::IOStreamBuffer( size_t cache )
+: m_stream( nullptr )
+, m_filesize( 0 )
+, m_cacheSize( cache )
+, m_numBlocks( 0 )
+, m_blockIdx( 0 )
+, m_cachePos( 0 )
+, m_filePos( 0 ) {
+    m_cache.resize( cache );
+    std::fill( m_cache.begin(), m_cache.end(), '\n' );
+}
+
+template<class T>
+inline
+IOStreamBuffer<T>::~IOStreamBuffer() {
+    // empty
+}
+
+template<class T>
+inline
+bool IOStreamBuffer<T>::open( IOStream *stream ) {
+    //  file still opened!
+    if ( nullptr != m_stream ) {
+        return false;
+    }
+
+    //  Invalid stream pointer
+    if ( nullptr == stream ) {
+        return false;
+    }
+
+    m_stream = stream;
+    m_filesize = m_stream->FileSize();
+    if ( m_filesize == 0 ) {
+        return false;
+    }
+    if ( m_filesize < m_cacheSize ) {
+        m_cacheSize = m_filesize;
+    }
+
+    m_numBlocks = m_filesize / m_cacheSize;
+    if ( ( m_filesize % m_cacheSize ) > 0 ) {
+        m_numBlocks++;
+    }
+
+    return true;
+}
+
+template<class T>
+inline
+bool IOStreamBuffer<T>::close() {
+    if ( nullptr == m_stream ) {
+        return false;
+    }
+
+    // init counters and state vars
+    m_stream    = nullptr;
+    m_filesize  = 0;
+    m_numBlocks = 0;
+    m_blockIdx  = 0;
+    m_cachePos  = 0;
+    m_filePos   = 0;
+
+    return true;
+}
+
+template<class T>
+inline
+size_t IOStreamBuffer<T>::size() const {
+    return m_filesize;
+}
+
+template<class T>
+inline
+size_t IOStreamBuffer<T>::cacheSize() const {
+    return m_cacheSize;
+}
+
+template<class T>
+inline
+bool IOStreamBuffer<T>::readNextBlock() {
+    m_stream->Seek( m_filePos, aiOrigin_SET );
+    size_t readLen = m_stream->Read( &m_cache[ 0 ], sizeof( T ), m_cacheSize );
+    if ( readLen == 0 ) {
+        return false;
+    }
+    if ( readLen < m_cacheSize ) {
+        m_cacheSize = readLen;
+    }
+    m_filePos += m_cacheSize;
+    m_cachePos = 0;
+    m_blockIdx++;
+
+    return true;
+}
+
+template<class T>
+inline
+size_t IOStreamBuffer<T>::getNumBlocks() const {
+    return m_numBlocks;
+}
+
+template<class T>
+inline
+size_t IOStreamBuffer<T>::getCurrentBlockIndex() const {
+    return m_blockIdx;
+}
+
+template<class T>
+inline
+size_t IOStreamBuffer<T>::getFilePos() const {
+    return m_filePos;
+}
+
+template<class T>
+inline
+bool IOStreamBuffer<T>::getNextLine( std::vector<T> &buffer ) {
+    buffer.resize( m_cacheSize );
+    ::memset( &buffer[ 0 ], '\n', m_cacheSize );
+
+    if ( m_cachePos == m_cacheSize || 0 == m_filePos ) {
+        if ( !readNextBlock() ) {
+            return false;
+        }
+    }
+    size_t i = 0;
+    while ( !IsLineEnd( m_cache[ m_cachePos ] ) ) {
+        buffer[ i ] = m_cache[ m_cachePos ];
+        m_cachePos++;
+        i++;
+        if ( m_cachePos >= m_cacheSize ) {
+            if ( !readNextBlock() ) {
+                return false;
+            }
+        }
+    }
+    m_cachePos++;
+
+    return true;
+}
+
+} // !ns Assimp

+ 3 - 3
code/ObjFileData.h

@@ -104,7 +104,7 @@ struct Face
 
 // ------------------------------------------------------------------------------------------------
 //! \struct Object
-//! \brief  Stores all objects of an objfile object definition
+//! \brief  Stores all objects of an obj-file object definition
 struct Object
 {
     enum ObjectType
@@ -160,8 +160,8 @@ struct Material
     aiString textureSpecularity;
     aiString textureOpacity;
     aiString textureDisp;
-    enum TextureType
-    {
+
+    enum TextureType {
         TextureDiffuseType = 0,
         TextureSpecularType,
         TextureAmbientType,

+ 14 - 14
code/ObjFileImporter.cpp

@@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "ObjFileImporter.h"
 #include "ObjFileParser.h"
 #include "ObjFileData.h"
+#include "IOStreamBuffer.h"
 #include <memory>
 #include <assimp/Importer.hpp>
 #include <assimp/scene.h>
@@ -126,8 +127,11 @@ void ObjFileImporter::InternReadFile( const std::string &file, aiScene* pScene,
         throw DeadlyImportError( "OBJ-file is too small.");
     }
 
+    IOStreamBuffer<char> streamedBuffer;
+    streamedBuffer.open( fileStream.get() );
+
     // Allocate buffer and read file into it
-    TextFileToBuffer( fileStream.get(),m_Buffer);
+    //TextFileToBuffer( fileStream.get(),m_Buffer);
 
     // Get the model name
     std::string  modelName, folderName;
@@ -150,7 +154,7 @@ void ObjFileImporter::InternReadFile( const std::string &file, aiScene* pScene,
     const unsigned int updateProgressEveryBytes = 100 * 1024;
     const unsigned int progressTotal = (3*m_Buffer.size()/updateProgressEveryBytes);
     // process all '\'
-    std::vector<char> ::iterator iter = m_Buffer.begin();
+    /*std::vector<char> ::iterator iter = m_Buffer.begin();
     while (iter != m_Buffer.end())
     {
         if (*iter == '\\')
@@ -169,17 +173,19 @@ void ObjFileImporter::InternReadFile( const std::string &file, aiScene* pScene,
             m_progress->UpdateFileRead(++progress, progressTotal);
             progressCounter = 0;
         }
-    }
+    }*/
 
     // 1/3rd progress
     m_progress->UpdateFileRead(1, 3);
 
     // parse the file into a temporary representation
-    ObjFileParser parser(m_Buffer, modelName, pIOHandler, m_progress, file);
+    ObjFileParser parser( streamedBuffer, modelName, pIOHandler, m_progress, file);
 
     // And create the proper return structures out of it
     CreateDataFromImport(parser.GetModel(), pScene);
 
+    streamedBuffer.close();
+
     // Clean up allocated storage for the next import
     m_Buffer.clear();
 
@@ -291,9 +297,7 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile
 
 // ------------------------------------------------------------------------------------------------
 //  Create topology data
-aiMesh *ObjFileImporter::createTopology( const ObjFile::Model* pModel, const ObjFile::Object* pData,
-                                         unsigned int meshIndex )
-{
+aiMesh *ObjFileImporter::createTopology( const ObjFile::Model* pModel, const ObjFile::Object* pData, unsigned int meshIndex ) {
     // Checking preconditions
     ai_assert( NULL != pModel );
 
@@ -477,19 +481,15 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel,
             aiFace *pDestFace = &pMesh->mFaces[ outIndex ];
 
             const bool last = ( vertexIndex == pSourceFace->m_pVertices->size() - 1 );
-            if (pSourceFace->m_PrimitiveType != aiPrimitiveType_LINE || !last)
-            {
+            if (pSourceFace->m_PrimitiveType != aiPrimitiveType_LINE || !last) {
                 pDestFace->mIndices[ outVertexIndex ] = newIndex;
                 outVertexIndex++;
             }
 
-            if (pSourceFace->m_PrimitiveType == aiPrimitiveType_POINT)
-            {
+            if (pSourceFace->m_PrimitiveType == aiPrimitiveType_POINT) {
                 outIndex++;
                 outVertexIndex = 0;
-            }
-            else if (pSourceFace->m_PrimitiveType == aiPrimitiveType_LINE)
-            {
+            } else if (pSourceFace->m_PrimitiveType == aiPrimitiveType_LINE) {
                 outVertexIndex = 0;
 
                 if(!last)

+ 2 - 2
code/ObjFileImporter.h

@@ -50,8 +50,8 @@ struct aiNode;
 namespace Assimp {
 
 namespace ObjFile {
-struct Object;
-struct Model;
+    struct Object;
+    struct Model;
 }
 
 // ------------------------------------------------------------------------------------------------

+ 24 - 24
code/ObjFileParser.cpp

@@ -38,8 +38,6 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------
 */
-
-
 #ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
 
 #include "ObjFileParser.h"
@@ -54,16 +52,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/Importer.hpp>
 #include <cstdlib>
 
-
 namespace Assimp {
 
 const std::string ObjFileParser::DEFAULT_MATERIAL = AI_DEFAULT_MATERIAL_NAME;
 
 // -------------------------------------------------------------------
 //  Constructor with loaded data and directories.
-ObjFileParser::ObjFileParser(std::vector<char> &data, const std::string &modelName, IOSystem *io, ProgressHandler* progress, const std::string &originalObjFileName) :
-    m_DataIt(data.begin()),
-    m_DataItEnd(data.end()),
+ObjFileParser::ObjFileParser( IOStreamBuffer<char> &streamBuffer, const std::string &modelName, 
+                              IOSystem *io, ProgressHandler* progress,
+                              const std::string &originalObjFileName) :
+    m_DataIt(),
+    m_DataItEnd(),
     m_pModel(NULL),
     m_uiLine(0),
     m_pIO( io ),
@@ -83,7 +82,7 @@ ObjFileParser::ObjFileParser(std::vector<char> &data, const std::string &modelNa
     m_pModel->m_MaterialMap[ DEFAULT_MATERIAL ] = m_pModel->m_pDefaultMaterial;
 
     // Start parsing the file
-    parseFile();
+    parseFile( streamBuffer );
 }
 
 // -------------------------------------------------------------------
@@ -103,30 +102,31 @@ ObjFile::Model *ObjFileParser::GetModel() const
 
 // -------------------------------------------------------------------
 //  File parsing method.
-void ObjFileParser::parseFile()
-{
-    if (m_DataIt == m_DataItEnd)
-        return;
-
+void ObjFileParser::parseFile( IOStreamBuffer<char> &streamBuffer ) {
     // only update every 100KB or it'll be too slow
     const unsigned int updateProgressEveryBytes = 100 * 1024;
     unsigned int progressCounter = 0;
-    const unsigned int bytesToProcess = std::distance(m_DataIt, m_DataItEnd);
+    const unsigned int bytesToProcess = streamBuffer.size();
     const unsigned int progressTotal = 3 * bytesToProcess;
     const unsigned int progressOffset = bytesToProcess;
     unsigned int processed = 0;
+    size_t lastFilePos( 0 );
 
-    DataArrayIt lastDataIt = m_DataIt;
+    bool endOfFile( false );
+    std::vector<char> buffer;
 
-    while (m_DataIt != m_DataItEnd)
-    {
-        // Handle progress reporting
-        processed += std::distance(lastDataIt, m_DataIt);
-        lastDataIt = m_DataIt;
-        if (processed > (progressCounter * updateProgressEveryBytes))
-        {
+    //while ( m_DataIt != m_DataItEnd )
+    while ( streamBuffer.getNextLine( buffer ) ) {
+        m_DataIt = buffer.begin();
+        m_DataItEnd = buffer.end();
+
+        // Handle progress reporting        
+        const size_t filePos( streamBuffer.getFilePos() );
+        if ( lastFilePos < filePos ) {
+            processed += filePos;
+            lastFilePos = filePos;
             progressCounter++;
-            m_progress->UpdateFileRead(progressOffset + processed*2, progressTotal);
+            m_progress->UpdateFileRead( progressOffset + processed * 2, progressTotal );
         }
 
         // parse line
@@ -149,8 +149,8 @@ void ObjFileParser::parseFile()
                     }
                 } else if (*m_DataIt == 't') {
                     // read in texture coordinate ( 2D or 3D )
-                                        ++m_DataIt;
-                                        getVector( m_pModel->m_TextureCoord );
+                    ++m_DataIt;
+                    getVector( m_pModel->m_TextureCoord );
                 } else if (*m_DataIt == 'n') {
                     // Read in normal vector definition
                     ++m_DataIt;

+ 3 - 2
code/ObjFileParser.h

@@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/vector2.h>
 #include <assimp/vector3.h>
 #include <assimp/mesh.h>
+#include "IOStreamBuffer.h"
 
 namespace Assimp {
 
@@ -72,7 +73,7 @@ public:
 
 public:
     /// \brief  Constructor with data array.
-    ObjFileParser(std::vector<char> &Data, const std::string &strModelName, IOSystem* io, ProgressHandler* progress, const std::string &originalObjFileName);
+    ObjFileParser( IOStreamBuffer<char> &streamBuffer, const std::string &strModelName, IOSystem* io, ProgressHandler* progress, const std::string &originalObjFileName);
     /// \brief  Destructor
     ~ObjFileParser();
     /// \brief  Model getter.
@@ -80,7 +81,7 @@ public:
 
 private:
     /// Parse the loaded file
-    void parseFile();
+    void parseFile( IOStreamBuffer<char> &streamBuffer );
     /// Method to copy the new delimited word in the current line.
     void copyNextWord(char *pBuffer, size_t length);
     /// Method to copy the new line.

+ 17 - 3
code/ObjTools.h

@@ -142,15 +142,15 @@ inline char_t getName( char_t it, char_t end, std::string &name )
     }
 
     char *pStart = &( *it );
-    while( !isEndOfBuffer( it, end ) && !IsLineEnd( *it ) ) {
+    while( !isEndOfBuffer( it, end ) && !IsLineEnd( *it ) && !IsSpaceOrNewLine( *it ) ) {
         ++it;
     }
 
-    while( isEndOfBuffer( it, end ) || IsLineEnd( *it ) || IsSpaceOrNewLine( *it ) ) {
+    /*while( isEndOfBuffer( it, end ) || IsLineEnd( *it ) || IsSpaceOrNewLine( *it ) ) {
         --it;
     }
     ++it;
-
+    */
     // Get name
     // if there is no name, and the previous char is a separator, come back to start
     while (&(*it) < pStart) {
@@ -246,6 +246,20 @@ string_type trim_whitespaces(string_type str)
     return str;
 }
 
+template<class T>
+bool hasLineEnd( T it, T end ) {
+    bool hasLineEnd( false );
+    while ( !isEndOfBuffer( it, end ) ) {
+        it++;
+        if ( IsLineEnd( it ) ) {
+            hasLineEnd = true;
+            break;
+        }
+    }
+
+    return hasLineEnd;
+}
+
 } // Namespace Assimp
 
 #endif // OBJ_TOOLS_H_INC

+ 2 - 2
code/glTFExporter.cpp

@@ -447,7 +447,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aim, Ref<Mesh>& meshRef, Ref<Buffer
 
         unsigned int jointNamesIndex;
         bool addJointToJointNames = true;
-        for (int idx_joint = 0; idx_joint < skinRef->jointNames.size(); ++idx_joint) {
+        for ( unsigned int idx_joint = 0; idx_joint < skinRef->jointNames.size(); ++idx_joint) {
             if (skinRef->jointNames[idx_joint]->jointName.compare(nodeRef->jointName) == 0) {
                 addJointToJointNames = false;
                 jointNamesIndex = idx_joint;
@@ -732,7 +732,7 @@ void glTFExporter::ExportMeshes()
     // Create the Accessor for skinRef->inverseBindMatrices
     if (createSkin) {
         mat4* invBindMatrixData = new mat4[inverseBindMatricesData.size()];
-        for (int idx_joint = 0; idx_joint < inverseBindMatricesData.size(); ++idx_joint) {
+        for ( unsigned int idx_joint = 0; idx_joint < inverseBindMatricesData.size(); ++idx_joint) {
             CopyValue(inverseBindMatricesData[idx_joint], invBindMatrixData[idx_joint]);
         }
 

+ 1 - 0
test/CMakeLists.txt

@@ -70,6 +70,7 @@ SET( TEST_SRCS
   unit/utImporter.cpp
   unit/utImproveCacheLocality.cpp
   unit/utIOSystem.cpp
+  unit/utIOStreamBuffer.cpp
   unit/utIssues.cpp
   unit/utJoinVertices.cpp
   unit/utLimitBoneWeights.cpp

+ 62 - 0
test/unit/TestIOStream.h

@@ -0,0 +1,62 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2016, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#pragma once
+
+#include "DefaultIOStream.h"
+
+using namespace ::Assimp;
+
+class TestDefaultIOStream : public DefaultIOStream {
+public:
+    TestDefaultIOStream()
+        : DefaultIOStream() {
+        // empty
+    }
+
+    TestDefaultIOStream( FILE* pFile, const std::string &strFilename )
+    : DefaultIOStream( pFile, strFilename ) {
+        // empty
+    }
+
+    virtual ~TestDefaultIOStream() {
+        // empty
+    }
+};
+

+ 1 - 18
test/unit/utDefaultIOStream.cpp

@@ -37,7 +37,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 -------------------------------------------------------------------------*/
 #include <gtest/gtest.h>
-#include "DefaultIOStream.h"
+#include "TestIOStream.h"
 
 using namespace ::Assimp;
 
@@ -45,23 +45,6 @@ class utDefaultIOStream : public ::testing::Test {
     // empty
 };
 
-class TestDefaultIOStream : public DefaultIOStream {
-public:
-    TestDefaultIOStream()
-    : DefaultIOStream() {
-        // empty
-    }
-
-    TestDefaultIOStream( FILE* pFile, const std::string &strFilename )
-    : DefaultIOStream( pFile, strFilename ) {
-        // empty
-    }
-
-    virtual ~TestDefaultIOStream() {
-        // empty
-    }
-};
-
 TEST_F( utDefaultIOStream, FileSizeTest ) {
     char buffer[ L_tmpnam ];
     tmpnam( buffer );

+ 112 - 0
test/unit/utIOStreamBuffer.cpp

@@ -0,0 +1,112 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2016, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+#include "UnitTestPCH.h"
+#include "IOStreamBuffer.h"
+#include "TestIOStream.h"
+
+class IOStreamBufferTest : public ::testing::Test {
+    // empty
+};
+
+using namespace Assimp;
+
+TEST_F( IOStreamBufferTest, creationTest ) {
+    bool ok( true );
+    try {
+        IOStreamBuffer<char> myBuffer;
+    } catch ( ... ) {
+        ok = false;
+    }
+    EXPECT_TRUE( ok );
+}
+
+TEST_F( IOStreamBufferTest, accessCacheSizeTest ) {
+    IOStreamBuffer<char> myBuffer1;
+    EXPECT_NE( 0, myBuffer1.cacheSize() );
+
+    IOStreamBuffer<char> myBuffer2( 100 );
+    EXPECT_EQ( 100, myBuffer2.cacheSize() );
+}
+
+TEST_F( IOStreamBufferTest, open_close_Test ) {
+    IOStreamBuffer<char> myBuffer;
+
+    EXPECT_FALSE( myBuffer.open( nullptr ) );
+    EXPECT_FALSE( myBuffer.close() );
+
+    char buffer[ L_tmpnam ];
+    tmpnam( buffer );
+    std::FILE *fs( std::fopen( buffer, "w+" ) );
+    size_t written( std::fwrite( buffer, 1, sizeof( char ) * L_tmpnam, fs ) );
+    std::fflush( fs );
+
+    TestDefaultIOStream myStream( fs, buffer );
+
+    EXPECT_TRUE( myBuffer.open( &myStream ) );
+    EXPECT_FALSE( myBuffer.open( &myStream ) );
+    EXPECT_TRUE( myBuffer.close() );
+}
+
+TEST_F( IOStreamBufferTest, readlineTest ) {
+    char buffer[ L_tmpnam ];
+    tmpnam( buffer );
+    std::FILE *fs( std::fopen( buffer, "w+" ) );
+    size_t written( std::fwrite( buffer, 1, sizeof( char ) * L_tmpnam, fs ) );
+    std::fflush( fs );
+
+    IOStreamBuffer<char> myBuffer( 26 );
+    EXPECT_EQ( 26, myBuffer.cacheSize() );
+
+    TestDefaultIOStream myStream( fs, buffer );
+    size_t size( myStream.FileSize() );
+    size_t numBlocks( size / myBuffer.cacheSize() );
+    if ( size % myBuffer.cacheSize() > 0 ) {
+        numBlocks++;
+    }
+    EXPECT_TRUE( myBuffer.open( &myStream ) );
+    EXPECT_EQ( numBlocks, myBuffer.getNumBlocks() );
+    EXPECT_TRUE( myBuffer.close() );
+}
+
+TEST_F( IOStreamBufferTest, accessBlockIndexTest ) {
+
+}