Forráskód Böngészése

Add bounds checks to the parsing utilities. (#5421)

* Add bounds checks to the parsing utilities.

* Fix merge conflicts in ACLoader.

* Fix loaders

* Fix unittest of AC-Loader.

* Remove dead code.

* Md5Parser fixes

* Fix md5-parsing

* Fix Merge conflict

* Fix merge conflicts.

* Md5: Fix warning: missing return statement.
Kim Kulling 1 éve
szülő
commit
c08e3b4abb
39 módosított fájl, 854 hozzáadás és 783 törlés
  1. 93 100
      code/AssetLib/AC/ACLoader.cpp
  2. 2 2
      code/AssetLib/AC/ACLoader.h
  3. 36 25
      code/AssetLib/ASE/ASEParser.cpp
  4. 3 0
      code/AssetLib/ASE/ASEParser.h
  5. 21 17
      code/AssetLib/COB/COBLoader.cpp
  6. 1 1
      code/AssetLib/COB/COBLoader.h
  7. 39 38
      code/AssetLib/CSM/CSMLoader.cpp
  8. 42 26
      code/AssetLib/Collada/ColladaParser.cpp
  9. 39 31
      code/AssetLib/Irr/IRRMeshLoader.cpp
  10. 1 1
      code/AssetLib/Irr/IRRMeshLoader.h
  11. 7 5
      code/AssetLib/Irr/IRRShared.cpp
  12. 1 1
      code/AssetLib/LWO/LWOBLoader.cpp
  13. 77 80
      code/AssetLib/LWS/LWSLoader.cpp
  14. 1 1
      code/AssetLib/LWS/LWSLoader.h
  15. 17 15
      code/AssetLib/MD3/MD3Loader.cpp
  16. 8 8
      code/AssetLib/MD5/MD5Loader.cpp
  17. 3 3
      code/AssetLib/MD5/MD5Loader.h
  18. 124 98
      code/AssetLib/MD5/MD5Parser.cpp
  19. 31 29
      code/AssetLib/MD5/MD5Parser.h
  20. 31 36
      code/AssetLib/NFF/NFFLoader.cpp
  21. 17 16
      code/AssetLib/OFF/OFFLoader.cpp
  22. 11 8
      code/AssetLib/Obj/ObjFileParser.cpp
  23. 4 9
      code/AssetLib/Obj/ObjFileParser.h
  24. 3 2
      code/AssetLib/Ply/PlyLoader.cpp
  25. 33 28
      code/AssetLib/Ply/PlyParser.cpp
  26. 2 2
      code/AssetLib/Ply/PlyParser.h
  27. 5 4
      code/AssetLib/Raw/RawLoader.cpp
  28. 72 72
      code/AssetLib/SMD/SMDLoader.cpp
  29. 18 21
      code/AssetLib/SMD/SMDLoader.h
  30. 28 31
      code/AssetLib/STEPParser/STEPFileReader.cpp
  31. 1 2
      code/AssetLib/STEPParser/STEPFileReader.h
  32. 12 12
      code/AssetLib/STL/STLLoader.cpp
  33. 12 20
      code/AssetLib/Step/STEPFile.h
  34. 8 7
      code/AssetLib/Unreal/UnrealLoader.cpp
  35. 13 9
      code/AssetLib/XGL/XGLLoader.cpp
  36. 4 0
      code/CMakeLists.txt
  37. 3 2
      code/PostProcessing/ProcessHelper.cpp
  38. 14 4
      include/assimp/LineSplitter.h
  39. 17 17
      include/assimp/ParsingUtils.h

+ 93 - 100
code/AssetLib/AC/ACLoader.cpp

@@ -77,8 +77,8 @@ static constexpr aiImporterDesc desc = {
 
 // ------------------------------------------------------------------------------------------------
 // skip to the next token
-inline const char *AcSkipToNextToken(const char *buffer) {
-    if (!SkipSpaces(&buffer)) {
+inline const char *AcSkipToNextToken(const char *buffer, const char *end) {
+    if (!SkipSpaces(&buffer, end)) {
         ASSIMP_LOG_ERROR("AC3D: Unexpected EOF/EOL");
     }
     return buffer;
@@ -86,13 +86,13 @@ inline const char *AcSkipToNextToken(const char *buffer) {
 
 // ------------------------------------------------------------------------------------------------
 // read a string (may be enclosed in double quotation marks). buffer must point to "
-inline const char *AcGetString(const char *buffer, std::string &out) {
+inline const char *AcGetString(const char *buffer, const char *end, std::string &out) {
     if (*buffer == '\0') {
         throw DeadlyImportError("AC3D: Unexpected EOF in string");
     }
     ++buffer;
     const char *sz = buffer;
-    while ('\"' != *buffer) {
+    while ('\"' != *buffer && buffer != end) {
         if (IsLineEnd(*buffer)) {
             ASSIMP_LOG_ERROR("AC3D: Unexpected EOF/EOL in string");
             out = "ERROR";
@@ -112,8 +112,8 @@ inline const char *AcGetString(const char *buffer, std::string &out) {
 // ------------------------------------------------------------------------------------------------
 // read 1 to n floats prefixed with an optional predefined identifier
 template <class T>
-inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *name, size_t name_length, size_t num, T *out) {
-    buffer = AcSkipToNextToken(buffer);
+inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *end, const char *name, size_t name_length, size_t num, T *out) {
+    buffer = AcSkipToNextToken(buffer, end);
     if (0 != name_length) {
         if (0 != strncmp(buffer, name, name_length) || !IsSpace(buffer[name_length])) {
             ASSIMP_LOG_ERROR("AC3D: Unexpected token. ", name, " was expected.");
@@ -122,7 +122,7 @@ inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *name
         buffer += name_length + 1;
     }
     for (unsigned int _i = 0; _i < num; ++_i) {
-        buffer = AcSkipToNextToken(buffer);
+        buffer = AcSkipToNextToken(buffer, end);
         buffer = fast_atoreal_move<float>(buffer, ((float *)out)[_i]);
     }
 
@@ -132,7 +132,7 @@ inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *name
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 AC3DImporter::AC3DImporter() :
-        buffer(),
+        mBuffer(),
         configSplitBFCull(),
         configEvalSubdivision(),
         mNumMeshes(),
@@ -164,17 +164,17 @@ const aiImporterDesc *AC3DImporter::GetInfo() const {
 // ------------------------------------------------------------------------------------------------
 // Get a pointer to the next line from the file
 bool AC3DImporter::GetNextLine() {
-    SkipLine(&buffer);
-    return SkipSpaces(&buffer);
+    SkipLine(&mBuffer.data, mBuffer.end);
+    return SkipSpaces(&mBuffer.data, mBuffer.end);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Parse an object section in an AC file
 bool AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
-    if (!TokenMatch(buffer, "OBJECT", 6))
+    if (!TokenMatch(mBuffer.data, "OBJECT", 6))
         return false;
 
-    SkipSpaces(&buffer);
+    SkipSpaces(&mBuffer.data, mBuffer.end);
 
     ++mNumMeshes;
 
@@ -182,7 +182,7 @@ bool AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
     Object &obj = objects.back();
 
     aiLight *light = nullptr;
-    if (!ASSIMP_strincmp(buffer, "light", 5)) {
+    if (!ASSIMP_strincmp(mBuffer.data, "light", 5)) {
         // This is a light source. Add it to the list
         mLights->push_back(light = new aiLight());
 
@@ -198,16 +198,16 @@ bool AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
 
         ASSIMP_LOG_VERBOSE_DEBUG("AC3D: Light source encountered");
         obj.type = Object::Light;
-    } else if (!ASSIMP_strincmp(buffer, "group", 5)) {
+    } else if (!ASSIMP_strincmp(mBuffer.data, "group", 5)) {
         obj.type = Object::Group;
-    } else if (!ASSIMP_strincmp(buffer, "world", 5)) {
+    } else if (!ASSIMP_strincmp(mBuffer.data, "world", 5)) {
         obj.type = Object::World;
     } else
         obj.type = Object::Poly;
     while (GetNextLine()) {
-        if (TokenMatch(buffer, "kids", 4)) {
-            SkipSpaces(&buffer);
-            unsigned int num = strtoul10(buffer, &buffer);
+        if (TokenMatch(mBuffer.data, "kids", 4)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
+            unsigned int num = strtoul10(mBuffer.data, &mBuffer.data);
             GetNextLine();
             if (num) {
                 // load the children of this object recursively
@@ -220,51 +220,44 @@ bool AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
                 }
             }
             return true;
-        } else if (TokenMatch(buffer, "name", 4)) {
-            SkipSpaces(&buffer);
-            buffer = AcGetString(buffer, obj.name);
+        } else if (TokenMatch(mBuffer.data, "name", 4)) {
+            SkipSpaces(&mBuffer.data, mBuffer.data);
+            mBuffer.data = AcGetString(mBuffer.data, mBuffer.end, obj.name);
 
             // If this is a light source, we'll also need to store
             // the name of the node in it.
             if (light) {
                 light->mName.Set(obj.name);
             }
-        } else if (TokenMatch(buffer, "texture", 7)) {
-            SkipSpaces(&buffer);
-            // skip empty acc texture
-            if (*buffer != '\"') {
-                if (!TokenMatch(buffer, "empty_texture_no_mapping", 24)) {
-                    ASSIMP_LOG_ERROR("AC3D: Unquoted texture string");
-                }
-            } else {
-                std::string texture;
-                buffer = AcGetString(buffer, texture);
-                obj.textures.push_back(texture);
-            }
-        } else if (TokenMatch(buffer, "texrep", 6)) {
-            SkipSpaces(&buffer);
-            buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 2, &obj.texRepeat);
+        } else if (TokenMatch(mBuffer.data, "texture", 7)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
+            std::string texture;
+            mBuffer.data = AcGetString(mBuffer.data, mBuffer.end, texture);
+            obj.textures.push_back(texture);
+        } else if (TokenMatch(mBuffer.data, "texrep", 6)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 2, &obj.texRepeat);
             if (!obj.texRepeat.x || !obj.texRepeat.y)
                 obj.texRepeat = aiVector2D(1.f, 1.f);
-        } else if (TokenMatch(buffer, "texoff", 6)) {
-            SkipSpaces(&buffer);
-            buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 2, &obj.texOffset);
-        } else if (TokenMatch(buffer, "rot", 3)) {
-            SkipSpaces(&buffer);
-            buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 9, &obj.rotation);
-        } else if (TokenMatch(buffer, "loc", 3)) {
-            SkipSpaces(&buffer);
-            buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 3, &obj.translation);
-        } else if (TokenMatch(buffer, "subdiv", 6)) {
-            SkipSpaces(&buffer);
-            obj.subDiv = strtoul10(buffer, &buffer);
-        } else if (TokenMatch(buffer, "crease", 6)) {
-            SkipSpaces(&buffer);
-            obj.crease = fast_atof(buffer);
-        } else if (TokenMatch(buffer, "numvert", 7)) {
-            SkipSpaces(&buffer);
-
-            unsigned int t = strtoul10(buffer, &buffer);
+        } else if (TokenMatch(mBuffer.data, "texoff", 6)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 2, &obj.texOffset);
+        } else if (TokenMatch(mBuffer.data, "rot", 3)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 9, &obj.rotation);
+        } else if (TokenMatch(mBuffer.data, "loc", 3)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 3, &obj.translation);
+        } else if (TokenMatch(mBuffer.data, "subdiv", 6)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
+            obj.subDiv = strtoul10(mBuffer.data, &mBuffer.data);
+        } else if (TokenMatch(mBuffer.data, "crease", 6)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
+            obj.crease = fast_atof(mBuffer.data);
+        } else if (TokenMatch(mBuffer.data, "numvert", 7)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
+
+            unsigned int t = strtoul10(mBuffer.data, &mBuffer.data);
             if (t >= AI_MAX_ALLOC(aiVector3D)) {
                 throw DeadlyImportError("AC3D: Too many vertices, would run out of memory");
             }
@@ -273,59 +266,59 @@ bool AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
                 if (!GetNextLine()) {
                     ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: not all vertices have been parsed yet");
                     break;
-                } else if (!IsNumeric(*buffer)) {
+                } else if (!IsNumeric(*mBuffer.data)) {
                     ASSIMP_LOG_ERROR("AC3D: Unexpected token: not all vertices have been parsed yet");
-                    --buffer; // make sure the line is processed a second time
+                    --mBuffer.data; // make sure the line is processed a second time
                     break;
                 }
                 obj.vertices.emplace_back();
                 aiVector3D &v = obj.vertices.back();
-                buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 3, &v.x);
+                mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 3, &v.x);
             }
-        } else if (TokenMatch(buffer, "numsurf", 7)) {
-            SkipSpaces(&buffer);
+        } else if (TokenMatch(mBuffer.data, "numsurf", 7)) {
+            SkipSpaces(&mBuffer.data, mBuffer.end);
 
             bool Q3DWorkAround = false;
 
-            const unsigned int t = strtoul10(buffer, &buffer);
+            const unsigned int t = strtoul10(mBuffer.data, &mBuffer.data);
             obj.surfaces.reserve(t);
             for (unsigned int i = 0; i < t; ++i) {
                 GetNextLine();
-                if (!TokenMatch(buffer, "SURF", 4)) {
+                if (!TokenMatch(mBuffer.data, "SURF", 4)) {
                     // FIX: this can occur for some files - Quick 3D for
                     // example writes no surf chunks
                     if (!Q3DWorkAround) {
                         ASSIMP_LOG_WARN("AC3D: SURF token was expected");
                         ASSIMP_LOG_VERBOSE_DEBUG("Continuing with Quick3D Workaround enabled");
                     }
-                    --buffer; // make sure the line is processed a second time
+                    --mBuffer.data; // make sure the line is processed a second time
                     // break; --- see fix notes above
 
                     Q3DWorkAround = true;
                 }
-                SkipSpaces(&buffer);
+                SkipSpaces(&mBuffer.data, mBuffer.end);
                 obj.surfaces.emplace_back();
                 Surface &surf = obj.surfaces.back();
-                surf.flags = strtoul_cppstyle(buffer);
+                surf.flags = strtoul_cppstyle(mBuffer.data);
 
                 while (true) {
                     if (!GetNextLine()) {
                         throw DeadlyImportError("AC3D: Unexpected EOF: surface is incomplete");
                     }
-                    if (TokenMatch(buffer, "mat", 3)) {
-                        SkipSpaces(&buffer);
-                        surf.mat = strtoul10(buffer);
-                    } else if (TokenMatch(buffer, "refs", 4)) {
+                    if (TokenMatch(mBuffer.data, "mat", 3)) {
+                        SkipSpaces(&mBuffer.data, mBuffer.end);
+                        surf.mat = strtoul10(mBuffer.data);
+                    } else if (TokenMatch(mBuffer.data, "refs", 4)) {
                         // --- see fix notes above
                         if (Q3DWorkAround) {
                             if (!surf.entries.empty()) {
-                                buffer -= 6;
+                                mBuffer.data -= 6;
                                 break;
                             }
                         }
 
-                        SkipSpaces(&buffer);
-                        const unsigned int m = strtoul10(buffer);
+                        SkipSpaces(&mBuffer.data, mBuffer.end);
+                        const unsigned int m = strtoul10(mBuffer.data);
                         surf.entries.reserve(m);
 
                         obj.numRefs += m;
@@ -338,12 +331,12 @@ bool AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
                             surf.entries.emplace_back();
                             Surface::SurfaceEntry &entry = surf.entries.back();
 
-                            entry.first = strtoul10(buffer, &buffer);
-                            SkipSpaces(&buffer);
-                            buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 2, &entry.second);
+                            entry.first = strtoul10(mBuffer.data, &mBuffer.data);
+                            SkipSpaces(&mBuffer.data, mBuffer.end);
+                            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 2, &entry.second);
                         }
                     } else {
-                        --buffer; // make sure the line is processed a second time
+                        --mBuffer.data; // make sure the line is processed a second time
                         break;
                     }
                 }
@@ -475,16 +468,15 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
                 }
 
                 switch ((*it).GetType()) {
-                    // closed line
-                case Surface::ClosedLine:
-                    needMat[idx].first += (unsigned int)(*it).entries.size();
-                    needMat[idx].second += (unsigned int)(*it).entries.size() << 1u;
+                case Surface::ClosedLine: // closed line
+                    needMat[idx].first += static_cast<unsigned int>((*it).entries.size());
+                    needMat[idx].second += static_cast<unsigned int>((*it).entries.size() << 1u);
                     break;
 
                     // unclosed line
                 case Surface::OpenLine:
-                    needMat[idx].first += (unsigned int)(*it).entries.size() - 1;
-                    needMat[idx].second += ((unsigned int)(*it).entries.size() - 1) << 1u;
+                    needMat[idx].first += static_cast<unsigned int>((*it).entries.size() - 1);
+                    needMat[idx].second += static_cast<unsigned int>(((*it).entries.size() - 1) << 1u);
                     break;
 
                     // triangle strip
@@ -763,17 +755,18 @@ void AC3DImporter::InternReadFile(const std::string &pFile,
     std::vector<char> mBuffer2;
     TextFileToBuffer(file.get(), mBuffer2);
 
-    buffer = &mBuffer2[0];
+    mBuffer.data = &mBuffer2[0];
+    mBuffer.end = &mBuffer2[0] + mBuffer2.size();
     mNumMeshes = 0;
 
     mLightsCounter = mPolysCounter = mWorldsCounter = mGroupsCounter = 0;
 
-    if (::strncmp(buffer, "AC3D", 4)) {
+    if (::strncmp(mBuffer.data, "AC3D", 4)) {
         throw DeadlyImportError("AC3D: No valid AC3D file, magic sequence not found");
     }
 
     // print the file format version to the console
-    unsigned int version = HexDigitToDecimal(buffer[4]);
+    unsigned int version = HexDigitToDecimal(mBuffer.data[4]);
     char msg[3];
     ASSIMP_itoa10(msg, 3, version);
     ASSIMP_LOG_INFO("AC3D file format version: ", msg);
@@ -788,31 +781,31 @@ void AC3DImporter::InternReadFile(const std::string &pFile,
     mLights = &lights;
 
     while (GetNextLine()) {
-        if (TokenMatch(buffer, "MATERIAL", 8)) {
+        if (TokenMatch(mBuffer.data, "MATERIAL", 8)) {
             materials.emplace_back();
             Material &mat = materials.back();
 
             // manually parse the material ... sscanf would use the buldin atof ...
             // Format: (name) rgb %f %f %f  amb %f %f %f  emis %f %f %f  spec %f %f %f  shi %d  trans %f
 
-            buffer = AcSkipToNextToken(buffer);
-            if ('\"' == *buffer) {
-                buffer = AcGetString(buffer, mat.name);
-                buffer = AcSkipToNextToken(buffer);
+            mBuffer.data = AcSkipToNextToken(mBuffer.data, mBuffer.end);
+            if ('\"' == *mBuffer.data) {
+                mBuffer.data = AcGetString(mBuffer.data, mBuffer.end, mat.name);
+                mBuffer.data = AcSkipToNextToken(mBuffer.data, mBuffer.end);
             }
 
-            buffer = TAcCheckedLoadFloatArray(buffer, "rgb", 3, 3, &mat.rgb);
-            buffer = TAcCheckedLoadFloatArray(buffer, "amb", 3, 3, &mat.amb);
-            buffer = TAcCheckedLoadFloatArray(buffer, "emis", 4, 3, &mat.emis);
-            buffer = TAcCheckedLoadFloatArray(buffer, "spec", 4, 3, &mat.spec);
-            buffer = TAcCheckedLoadFloatArray(buffer, "shi", 3, 1, &mat.shin);
-            buffer = TAcCheckedLoadFloatArray(buffer, "trans", 5, 1, &mat.trans);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "rgb", 3, 3, &mat.rgb);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "amb", 3, 3, &mat.amb);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "emis", 4, 3, &mat.emis);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "spec", 4, 3, &mat.spec);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "shi", 3, 1, &mat.shin);
+            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "trans", 5, 1, &mat.trans);
         } else {
             LoadObjectSection(rootObjects);
         }
     }
 
-    if (rootObjects.empty() || !mNumMeshes) {
+    if (rootObjects.empty() || mNumMeshes == 0u) {
         throw DeadlyImportError("AC3D: No meshes have been loaded");
     }
     if (materials.empty()) {
@@ -828,7 +821,7 @@ void AC3DImporter::InternReadFile(const std::string &pFile,
     materials.reserve(mNumMeshes);
 
     // generate a dummy root if there are multiple objects on the top layer
-    Object *root;
+    Object *root = nullptr;
     if (1 == rootObjects.size())
         root = &rootObjects[0];
     else {
@@ -841,7 +834,7 @@ void AC3DImporter::InternReadFile(const std::string &pFile,
         delete root;
     }
 
-    if (!::strncmp(pScene->mRootNode->mName.data, "Node", 4)) {
+    if (::strncmp(pScene->mRootNode->mName.data, "Node", 4) == 0) {
         pScene->mRootNode->mName.Set("<AC3DWorld>");
     }
 
@@ -860,7 +853,7 @@ void AC3DImporter::InternReadFile(const std::string &pFile,
 
     // copy lights
     pScene->mNumLights = (unsigned int)lights.size();
-    if (lights.size()) {
+    if (!lights.empty()) {
         pScene->mLights = new aiLight *[lights.size()];
         ::memcpy(pScene->mLights, &lights[0], lights.size() * sizeof(void *));
     }

+ 2 - 2
code/AssetLib/AC/ACLoader.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2024, assimp team
 
 All rights reserved.
 
@@ -242,7 +242,7 @@ private:
 
 private:
     // points to the next data line
-    const char *buffer;
+    aiBuffer mBuffer;
 
     // Configuration option: if enabled, up to two meshes
     // are generated per material: those faces who have

+ 36 - 25
code/AssetLib/ASE/ASEParser.cpp

@@ -110,10 +110,12 @@ using namespace Assimp::ASE;
     ++filePtr;
 
 // ------------------------------------------------------------------------------------------------
-Parser::Parser(const char *szFile, unsigned int fileFormatDefault) {
+Parser::Parser(const char *szFile, unsigned int fileFormatDefault) :
+        filePtr(nullptr), mEnd (nullptr) {
     ai_assert(nullptr != szFile);
 
     filePtr = szFile;
+    mEnd = filePtr + std::strlen(filePtr);
     iFileFormat = fileFormatDefault;
 
     // make sure that the color values are invalid
@@ -179,14 +181,22 @@ bool Parser::SkipToNextToken() {
     while (true) {
         char me = *filePtr;
 
+        if (filePtr == mEnd) {
+            return false;
+        }
+
         // increase the line number counter if necessary
         if (IsLineEnd(me) && !bLastWasEndLine) {
             ++iLineNumber;
             bLastWasEndLine = true;
         } else
             bLastWasEndLine = false;
-        if ('*' == me || '}' == me || '{' == me) return true;
-        if ('\0' == me) return false;
+        if ('*' == me || '}' == me || '{' == me) {
+            return true;
+        }
+        if ('\0' == me) {
+            return false;
+        }
 
         ++filePtr;
     }
@@ -344,8 +354,9 @@ void Parser::ParseLV1SoftSkinBlock() {
             unsigned int numVerts = 0;
 
             const char *sz = filePtr;
-            while (!IsSpaceOrNewLine(*filePtr))
+            while (!IsSpaceOrNewLine(*filePtr)) {
                 ++filePtr;
+            }
 
             const unsigned int diff = (unsigned int)(filePtr - sz);
             if (diff) {
@@ -363,24 +374,24 @@ void Parser::ParseLV1SoftSkinBlock() {
                     // Skip the mesh data - until we find a new mesh
                     // or the end of the *MESH_SOFTSKINVERTS section
                     while (true) {
-                        SkipSpacesAndLineEnd(&filePtr);
+                        SkipSpacesAndLineEnd(&filePtr, mEnd);
                         if (*filePtr == '}') {
                             ++filePtr;
                             return;
                         } else if (!IsNumeric(*filePtr))
                             break;
 
-                        SkipLine(&filePtr);
+                        SkipLine(&filePtr, mEnd);
                     }
                 } else {
-                    SkipSpacesAndLineEnd(&filePtr);
+                    SkipSpacesAndLineEnd(&filePtr, mEnd);
                     ParseLV4MeshLong(numVerts);
 
                     // Reserve enough storage
                     curMesh->mBoneVertices.reserve(numVerts);
 
                     for (unsigned int i = 0; i < numVerts; ++i) {
-                        SkipSpacesAndLineEnd(&filePtr);
+                        SkipSpacesAndLineEnd(&filePtr, mEnd);
                         unsigned int numWeights;
                         ParseLV4MeshLong(numWeights);
 
@@ -422,7 +433,7 @@ void Parser::ParseLV1SoftSkinBlock() {
         if (*filePtr == '\0')
             return;
         ++filePtr;
-        SkipSpacesAndLineEnd(&filePtr);
+        SkipSpacesAndLineEnd(&filePtr, mEnd);
     }
 }
 
@@ -743,7 +754,7 @@ void Parser::ParseLV3MapBlock(Texture &map) {
 // ------------------------------------------------------------------------------------------------
 bool Parser::ParseString(std::string &out, const char *szName) {
     char szBuffer[1024];
-    if (!SkipSpaces(&filePtr)) {
+    if (!SkipSpaces(&filePtr, mEnd)) {
 
         ai_snprintf(szBuffer, 1024, "Unable to parse %s block: Unexpected EOL", szName);
         LogWarning(szBuffer);
@@ -1355,7 +1366,7 @@ void Parser::ParseLV4MeshBones(unsigned int iNumBones, ASE::Mesh &mesh) {
             // Mesh bone with name ...
             if (TokenMatch(filePtr, "MESH_BONE_NAME", 14)) {
                 // parse an index ...
-                if (SkipSpaces(&filePtr)) {
+                if (SkipSpaces(&filePtr, mEnd)) {
                     unsigned int iIndex = strtoul10(filePtr, &filePtr);
                     if (iIndex >= iNumBones) {
                         LogWarning("Bone index is out of bounds");
@@ -1395,11 +1406,11 @@ void Parser::ParseLV4MeshBonesVertices(unsigned int iNumVertices, ASE::Mesh &mes
                 std::pair<int, float> pairOut;
                 while (true) {
                     // first parse the bone index ...
-                    if (!SkipSpaces(&filePtr)) break;
+                    if (!SkipSpaces(&filePtr, mEnd)) break;
                     pairOut.first = strtoul10(filePtr, &filePtr);
 
                     // then parse the vertex weight
-                    if (!SkipSpaces(&filePtr)) break;
+                    if (!SkipSpaces(&filePtr, mEnd)) break;
                     filePtr = fast_atoreal_move<float>(filePtr, pairOut.second);
 
                     // -1 marks unused entries
@@ -1675,7 +1686,7 @@ void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh &sMesh) {
 // ------------------------------------------------------------------------------------------------
 void Parser::ParseLV4MeshFace(ASE::Face &out) {
     // skip spaces and tabs
-    if (!SkipSpaces(&filePtr)) {
+    if (!SkipSpaces(&filePtr, mEnd)) {
         LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL [#1]");
         SkipToNextToken();
         return;
@@ -1685,7 +1696,7 @@ void Parser::ParseLV4MeshFace(ASE::Face &out) {
     out.iFace = strtoul10(filePtr, &filePtr);
 
     // next character should be ':'
-    if (!SkipSpaces(&filePtr)) {
+    if (!SkipSpaces(&filePtr, mEnd)) {
         // FIX: there are some ASE files which haven't got : here ....
         LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. \':\' expected [#2]");
         SkipToNextToken();
@@ -1697,7 +1708,7 @@ void Parser::ParseLV4MeshFace(ASE::Face &out) {
     // Parse all mesh indices
     for (unsigned int i = 0; i < 3; ++i) {
         unsigned int iIndex = 0;
-        if (!SkipSpaces(&filePtr)) {
+        if (!SkipSpaces(&filePtr, mEnd)) {
             LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL");
             SkipToNextToken();
             return;
@@ -1723,7 +1734,7 @@ void Parser::ParseLV4MeshFace(ASE::Face &out) {
         ++filePtr;
 
         // next character should be ':'
-        if (!SkipSpaces(&filePtr) || ':' != *filePtr) {
+        if (!SkipSpaces(&filePtr, mEnd) || ':' != *filePtr) {
             LogWarning("Unable to parse *MESH_FACE Element: "
                        "Unexpected EOL. \':\' expected [#2]");
             SkipToNextToken();
@@ -1731,9 +1742,9 @@ void Parser::ParseLV4MeshFace(ASE::Face &out) {
         }
 
         ++filePtr;
-        if (!SkipSpaces(&filePtr)) {
+        if (!SkipSpaces(&filePtr, mEnd)) {
             LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. "
-                       "Vertex index ecpected [#4]");
+                       "Vertex index expected [#4]");
             SkipToNextToken();
             return;
         }
@@ -1752,7 +1763,7 @@ void Parser::ParseLV4MeshFace(ASE::Face &out) {
 
     // parse the smoothing group of the face
     if (TokenMatch(filePtr, "*MESH_SMOOTHING", 15)) {
-        if (!SkipSpaces(&filePtr)) {
+        if (!SkipSpaces(&filePtr, mEnd)) {
             LogWarning("Unable to parse *MESH_SMOOTHING Element: "
                        "Unexpected EOL. Smoothing group(s) expected [#5]");
             SkipToNextToken();
@@ -1771,12 +1782,12 @@ void Parser::ParseLV4MeshFace(ASE::Face &out) {
                     LogWarning(message.c_str());
                 }
             }
-            SkipSpaces(&filePtr);
+            SkipSpaces(&filePtr, mEnd);
             if (',' != *filePtr) {
                 break;
             }
             ++filePtr;
-            SkipSpaces(&filePtr);
+            SkipSpaces(&filePtr, mEnd);
         }
     }
 
@@ -1792,7 +1803,7 @@ void Parser::ParseLV4MeshFace(ASE::Face &out) {
     }
 
     if (TokenMatch(filePtr, "*MESH_MTLID", 11)) {
-        if (!SkipSpaces(&filePtr)) {
+        if (!SkipSpaces(&filePtr, mEnd)) {
             LogWarning("Unable to parse *MESH_MTLID Element: Unexpected EOL. "
                        "Material index expected [#6]");
             SkipToNextToken();
@@ -1840,7 +1851,7 @@ void Parser::ParseLV4MeshFloatTriple(ai_real *apOut) {
 // ------------------------------------------------------------------------------------------------
 void Parser::ParseLV4MeshFloat(ai_real &fOut) {
     // skip spaces and tabs
-    if (!SkipSpaces(&filePtr)) {
+    if (!SkipSpaces(&filePtr, mEnd)) {
         // LOG
         LogWarning("Unable to parse float: unexpected EOL [#1]");
         fOut = 0.0;
@@ -1853,7 +1864,7 @@ void Parser::ParseLV4MeshFloat(ai_real &fOut) {
 // ------------------------------------------------------------------------------------------------
 void Parser::ParseLV4MeshLong(unsigned int &iOut) {
     // Skip spaces and tabs
-    if (!SkipSpaces(&filePtr)) {
+    if (!SkipSpaces(&filePtr, mEnd)) {
         // LOG
         LogWarning("Unable to parse long: unexpected EOL [#1]");
         iOut = 0;

+ 3 - 0
code/AssetLib/ASE/ASEParser.h

@@ -620,6 +620,9 @@ public:
     //! Pointer to current data
     const char *filePtr;
 
+    /// The end pointer of the file data
+    const char *mEnd;
+
     //! background color to be passed to the viewer
     //! QNAN if none was found
     aiColor3D m_clrBackground;

+ 21 - 17
code/AssetLib/COB/COBLoader.cpp

@@ -473,8 +473,9 @@ void COBImporter::ReadBasicNodeInfo_Ascii(Node &msh, LineSplitter &splitter, con
         } else if (splitter.match_start("Transform")) {
             for (unsigned int y = 0; y < 4 && ++splitter; ++y) {
                 const char *s = splitter->c_str();
+                const char *end = s + splitter->size();
                 for (unsigned int x = 0; x < 4; ++x) {
-                    SkipSpaces(&s);
+                    SkipSpaces(&s, end);
                     msh.transform[y][x] = fast_atof(&s);
                 }
             }
@@ -486,12 +487,12 @@ void COBImporter::ReadBasicNodeInfo_Ascii(Node &msh, LineSplitter &splitter, con
 
 // ------------------------------------------------------------------------------------------------
 template <typename T>
-void COBImporter::ReadFloat3Tuple_Ascii(T &fill, const char **in) {
+void COBImporter::ReadFloat3Tuple_Ascii(T &fill, const char **in, const char *end) {
     const char *rgb = *in;
     for (unsigned int i = 0; i < 3; ++i) {
-        SkipSpaces(&rgb);
+        SkipSpaces(&rgb, end);
         if (*rgb == ',') ++rgb;
-        SkipSpaces(&rgb);
+        SkipSpaces(&rgb, end);
 
         fill[i] = fast_atof(&rgb);
     }
@@ -538,7 +539,7 @@ void COBImporter::ReadMat1_Ascii(Scene &out, LineSplitter &splitter, const Chunk
     }
 
     const char *rgb = splitter[1];
-    ReadFloat3Tuple_Ascii(mat.rgb, &rgb);
+    ReadFloat3Tuple_Ascii(mat.rgb, &rgb, splitter.getEnd());
 
     ++splitter;
     if (!splitter.match_start("alpha ")) {
@@ -617,20 +618,21 @@ void COBImporter::ReadLght_Ascii(Scene &out, LineSplitter &splitter, const Chunk
     }
 
     const char *rgb = splitter[1];
-    ReadFloat3Tuple_Ascii(msh.color, &rgb);
+    const char *end = splitter.getEnd();
+    ReadFloat3Tuple_Ascii(msh.color, &rgb, end);
 
-    SkipSpaces(&rgb);
+    SkipSpaces(&rgb, end);
     if (strncmp(rgb, "cone angle", 10) != 0) {
         ASSIMP_LOG_WARN("Expected `cone angle` entity in `color` line in `Lght` chunk ", nfo.id);
     }
-    SkipSpaces(rgb + 10, &rgb);
+    SkipSpaces(rgb + 10, &rgb, end);
     msh.angle = fast_atof(&rgb);
 
-    SkipSpaces(&rgb);
+    SkipSpaces(&rgb, end);
     if (strncmp(rgb, "inner angle", 11) != 0) {
         ASSIMP_LOG_WARN("Expected `inner angle` entity in `color` line in `Lght` chunk ", nfo.id);
     }
-    SkipSpaces(rgb + 11, &rgb);
+    SkipSpaces(rgb + 11, &rgb, end);
     msh.inner_angle = fast_atof(&rgb);
 
     // skip the rest for we can't handle this kind of physically-based lighting information.
@@ -703,14 +705,14 @@ void COBImporter::ReadPolH_Ascii(Scene &out, LineSplitter &splitter, const Chunk
 
             for (unsigned int cur = 0; cur < cnt && ++splitter; ++cur) {
                 const char *s = splitter->c_str();
-
+                const char *end = splitter.getEnd();
                 aiVector3D &v = msh.vertex_positions[cur];
 
-                SkipSpaces(&s);
+                SkipSpaces(&s, end);
                 v.x = fast_atof(&s);
-                SkipSpaces(&s);
+                SkipSpaces(&s, end);
                 v.y = fast_atof(&s);
-                SkipSpaces(&s);
+                SkipSpaces(&s, end);
                 v.z = fast_atof(&s);
             }
         } else if (splitter.match_start("Texture Vertices")) {
@@ -719,12 +721,13 @@ void COBImporter::ReadPolH_Ascii(Scene &out, LineSplitter &splitter, const Chunk
 
             for (unsigned int cur = 0; cur < cnt && ++splitter; ++cur) {
                 const char *s = splitter->c_str();
+                const char *end = splitter.getEnd();
 
                 aiVector2D &v = msh.texture_coords[cur];
 
-                SkipSpaces(&s);
+                SkipSpaces(&s, end);
                 v.x = fast_atof(&s);
-                SkipSpaces(&s);
+                SkipSpaces(&s, end);
                 v.y = fast_atof(&s);
             }
         } else if (splitter.match_start("Faces")) {
@@ -749,8 +752,9 @@ void COBImporter::ReadPolH_Ascii(Scene &out, LineSplitter &splitter, const Chunk
                 face.material = strtoul10(splitter[6]);
 
                 const char *s = (++splitter)->c_str();
+                const char *end = splitter.getEnd();
                 for (size_t i = 0; i < face.indices.size(); ++i) {
-                    if (!SkipSpaces(&s)) {
+                    if (!SkipSpaces(&s, end)) {
                         ThrowException("Expected EOL token in Face entry");
                     }
                     if ('<' != *s++) {

+ 1 - 1
code/AssetLib/COB/COBLoader.h

@@ -120,7 +120,7 @@ private:
     void ReadChunkInfo_Ascii(COB::ChunkInfo &out, const LineSplitter &splitter);
     void ReadBasicNodeInfo_Ascii(COB::Node &msh, LineSplitter &splitter, const COB::ChunkInfo &nfo);
     template <typename T>
-    void ReadFloat3Tuple_Ascii(T &fill, const char **in);
+    void ReadFloat3Tuple_Ascii(T &fill, const char **in, const char *end);
 
     void ReadPolH_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
     void ReadBitM_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);

+ 39 - 38
code/AssetLib/CSM/CSMLoader.cpp

@@ -82,23 +82,20 @@ CSMImporter::CSMImporter() : noSkeletonMesh(){
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
-bool CSMImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/) const
-{
+bool CSMImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/) const {
     static const char* tokens[] = {"$Filename"};
     return SearchFileHeaderForToken(pIOHandler,pFile,tokens,AI_COUNT_OF(tokens));
 }
 
 // ------------------------------------------------------------------------------------------------
 // Build a string of all file extensions supported
-const aiImporterDesc* CSMImporter::GetInfo () const
-{
+const aiImporterDesc* CSMImporter::GetInfo () const {
     return &desc;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Setup configuration properties for the loader
-void CSMImporter::SetupProperties(const Importer* pImp)
-{
+void CSMImporter::SetupProperties(const Importer* pImp) {
     noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0;
 }
 
@@ -118,29 +115,29 @@ void CSMImporter::InternReadFile( const std::string& pFile,
     std::vector<char> mBuffer2;
     TextFileToBuffer(file.get(),mBuffer2);
     const char* buffer = &mBuffer2[0];
-
+    const char *end = &mBuffer2[mBuffer2.size() - 1] + 1;
     std::unique_ptr<aiAnimation> anim(new aiAnimation());
     int first = 0, last = 0x00ffffff;
 
     // now process the file and look out for '$' sections
     while (true) {
-        SkipSpaces(&buffer);
+        SkipSpaces(&buffer, end);
         if ('\0' == *buffer)
             break;
 
         if ('$'  == *buffer)    {
             ++buffer;
             if (TokenMatchI(buffer,"firstframe",10))    {
-                SkipSpaces(&buffer);
+                SkipSpaces(&buffer, end);
                 first = strtol10(buffer,&buffer);
             }
             else if (TokenMatchI(buffer,"lastframe",9))     {
-                SkipSpaces(&buffer);
+                SkipSpaces(&buffer, end);
                 last = strtol10(buffer,&buffer);
             }
             else if (TokenMatchI(buffer,"rate",4))  {
-                SkipSpaces(&buffer);
-                float d;
+                SkipSpaces(&buffer, end);
+                float d = { 0.0f };
                 buffer = fast_atoreal_move<float>(buffer,d);
                 anim->mTicksPerSecond = d;
             }
@@ -148,8 +145,8 @@ void CSMImporter::InternReadFile( const std::string& pFile,
                 std::vector< aiNodeAnim* > anims_temp;
                 anims_temp.reserve(30);
                 while (true) {
-                    SkipSpaces(&buffer);
-                    if (IsLineEnd(*buffer) && SkipSpacesAndLineEnd(&buffer) && *buffer == '$')
+                    SkipSpaces(&buffer, end);
+                    if (IsLineEnd(*buffer) && SkipSpacesAndLineEnd(&buffer, end) && *buffer == '$')
                         break; // next section
 
                     // Construct a new node animation channel and setup its name
@@ -157,41 +154,43 @@ void CSMImporter::InternReadFile( const std::string& pFile,
                     aiNodeAnim* nda = anims_temp.back();
 
                     char* ot = nda->mNodeName.data;
-                    while (!IsSpaceOrNewLine(*buffer))
+                    while (!IsSpaceOrNewLine(*buffer)) {
                         *ot++ = *buffer++;
+                    }
 
                     *ot = '\0';
                     nda->mNodeName.length = static_cast<ai_uint32>(ot-nda->mNodeName.data);
                 }
 
                 anim->mNumChannels = static_cast<unsigned int>(anims_temp.size());
-                if (!anim->mNumChannels)
+                if (!anim->mNumChannels) {
                     throw DeadlyImportError("CSM: Empty $order section");
+                }
 
                 // copy over to the output animation
                 anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
                 ::memcpy(anim->mChannels,&anims_temp[0],sizeof(aiNodeAnim*)*anim->mNumChannels);
-            }
-            else if (TokenMatchI(buffer,"points",6))    {
-                if (!anim->mNumChannels)
+            } else if (TokenMatchI(buffer,"points",6)) {
+                if (!anim->mNumChannels) {
                     throw DeadlyImportError("CSM: \'$order\' section is required to appear prior to \'$points\'");
+                }
 
                 // If we know how many frames we'll read, we can preallocate some storage
                 unsigned int alloc = 100;
-                if (last != 0x00ffffff)
-                {
+                if (last != 0x00ffffff) {
                     alloc = last-first;
                     alloc += alloc>>2u; // + 25%
-                    for (unsigned int i = 0; i < anim->mNumChannels;++i)
+                    for (unsigned int i = 0; i < anim->mNumChannels; ++i) {
                         anim->mChannels[i]->mPositionKeys = new aiVectorKey[alloc];
+                    }
                 }
 
                 unsigned int filled = 0;
 
                 // Now read all point data.
                 while (true) {
-                    SkipSpaces(&buffer);
-                    if (IsLineEnd(*buffer) && (!SkipSpacesAndLineEnd(&buffer) || *buffer == '$'))   {
+                    SkipSpaces(&buffer, end);
+                    if (IsLineEnd(*buffer) && (!SkipSpacesAndLineEnd(&buffer, end) || *buffer == '$'))   {
                         break; // next section
                     }
 
@@ -202,8 +201,8 @@ void CSMImporter::InternReadFile( const std::string& pFile,
                     for (unsigned int i = 0; i < anim->mNumChannels;++i)    {
 
                         aiNodeAnim* s = anim->mChannels[i];
-                        if (s->mNumPositionKeys == alloc)   { /* need to reallocate? */
-
+                        if (s->mNumPositionKeys == alloc)   {
+                            // need to reallocate?
                             aiVectorKey* old = s->mPositionKeys;
                             s->mPositionKeys = new aiVectorKey[s->mNumPositionKeys = alloc*2];
                             ::memcpy(s->mPositionKeys,old,sizeof(aiVectorKey)*alloc);
@@ -211,24 +210,26 @@ void CSMImporter::InternReadFile( const std::string& pFile,
                         }
 
                         // read x,y,z
-                        if(!SkipSpacesAndLineEnd(&buffer))
+                        if (!SkipSpacesAndLineEnd(&buffer, end)) {
                             throw DeadlyImportError("CSM: Unexpected EOF occurred reading sample x coord");
+                        }
 
                         if (TokenMatchI(buffer, "DROPOUT", 7))  {
                             // seems this is invalid marker data; at least the doc says it's possible
                             ASSIMP_LOG_WARN("CSM: Encountered invalid marker data (DROPOUT)");
-                        }
-                        else    {
+                        } else {
                             aiVectorKey* sub = s->mPositionKeys + s->mNumPositionKeys;
                             sub->mTime = (double)frame;
                             buffer = fast_atoreal_move<float>(buffer, (float&)sub->mValue.x);
 
-                            if(!SkipSpacesAndLineEnd(&buffer))
+                            if (!SkipSpacesAndLineEnd(&buffer, end)) {
                                 throw DeadlyImportError("CSM: Unexpected EOF occurred reading sample y coord");
+                            }
                             buffer = fast_atoreal_move<float>(buffer, (float&)sub->mValue.y);
 
-                            if(!SkipSpacesAndLineEnd(&buffer))
+                            if (!SkipSpacesAndLineEnd(&buffer, end)) {
                                 throw DeadlyImportError("CSM: Unexpected EOF occurred reading sample z coord");
+                            }
                             buffer = fast_atoreal_move<float>(buffer, (float&)sub->mValue.z);
 
                             ++s->mNumPositionKeys;
@@ -236,22 +237,22 @@ void CSMImporter::InternReadFile( const std::string& pFile,
                     }
 
                     // update allocation granularity
-                    if (filled == alloc)
+                    if (filled == alloc) {
                         alloc *= 2;
+                    }
 
                     ++filled;
                 }
                 // all channels must be complete in order to continue safely.
                 for (unsigned int i = 0; i < anim->mNumChannels;++i)    {
-
-                    if (!anim->mChannels[i]->mNumPositionKeys)
+                    if (!anim->mChannels[i]->mNumPositionKeys) {
                         throw DeadlyImportError("CSM: Invalid marker track");
+                    }
                 }
             }
-        }
-        else    {
+        } else {
             // advance to the next line
-            SkipLine(&buffer);
+            SkipLine(&buffer, end);
         }
     }
 
@@ -265,7 +266,7 @@ void CSMImporter::InternReadFile( const std::string& pFile,
     pScene->mRootNode->mNumChildren = anim->mNumChannels;
     pScene->mRootNode->mChildren = new aiNode* [anim->mNumChannels];
 
-    for (unsigned int i = 0; i < anim->mNumChannels;++i)    {
+    for (unsigned int i = 0; i < anim->mNumChannels;++i) {
         aiNodeAnim* na = anim->mChannels[i];
 
         aiNode* nd  = pScene->mRootNode->mChildren[i] = new aiNode();

+ 42 - 26
code/AssetLib/Collada/ColladaParser.cpp

@@ -654,12 +654,13 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle
             std::string v;
             XmlParser::getValueAsString(currentNode, v);
             const char *content = v.c_str();
+            const char *end = content + v.size();
             for (unsigned int a = 0; a < 16; a++) {
-                SkipSpacesAndLineEnd(&content);
+                SkipSpacesAndLineEnd(&content, end);
                 // read a number
                 content = fast_atoreal_move<ai_real>(content, controller.mBindShapeMatrix[a]);
                 // skip whitespace after it
-                SkipSpacesAndLineEnd(&content);
+                SkipSpacesAndLineEnd(&content, end);
             }
         } else if (currentName == "source") {
             ReadSource(currentNode);
@@ -740,7 +741,9 @@ void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pC
                 throw DeadlyImportError("Unknown semantic \"", attrSemantic, "\" in <vertex_weights> data <input> element");
             }
         } else if (currentName == "vcount" && vertexCount > 0) {
-            const char *text = currentNode.text().as_string();
+            const std::string stdText = currentNode.text().as_string();
+            const char *text = stdText.c_str();
+            const char *end = text + stdText.size();
             size_t numWeights = 0;
             for (std::vector<size_t>::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it) {
                 if (*text == 0) {
@@ -749,7 +752,7 @@ void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pC
 
                 *it = strtoul10(text, &text);
                 numWeights += *it;
-                SkipSpacesAndLineEnd(&text);
+                SkipSpacesAndLineEnd(&text, end);
             }
             // reserve weight count
             pController.mWeights.resize(numWeights);
@@ -758,18 +761,19 @@ void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pC
             std::string stdText;
             XmlParser::getValueAsString(currentNode, stdText);
             const char *text = stdText.c_str();
+            const char *end = text + stdText.size();
             for (std::vector<std::pair<size_t, size_t>>::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it) {
                 if (text == nullptr) {
                     throw DeadlyImportError("Out of data while reading <vertex_weights>");
                 }
-                SkipSpacesAndLineEnd(&text);
+                SkipSpacesAndLineEnd(&text, end);
                 it->first = strtoul10(text, &text);
-                SkipSpacesAndLineEnd(&text);
+                SkipSpacesAndLineEnd(&text, end);
                 if (*text == 0) {
                     throw DeadlyImportError("Out of data while reading <vertex_weights>");
                 }
                 it->second = strtoul10(text, &text);
-                SkipSpacesAndLineEnd(&text);
+                SkipSpacesAndLineEnd(&text, end);
             }
         }
     }
@@ -952,15 +956,16 @@ void ColladaParser::ReadLight(XmlNode &node, Collada::Light &pLight) {
             std::string v;
             XmlParser::getValueAsString(currentNode, v);
             const char *content = v.c_str();
+            const char *end = content + v.size();
 
             content = fast_atoreal_move<ai_real>(content, (ai_real &)pLight.mColor.r);
-            SkipSpacesAndLineEnd(&content);
+            SkipSpacesAndLineEnd(&content, end);
 
             content = fast_atoreal_move<ai_real>(content, (ai_real &)pLight.mColor.g);
-            SkipSpacesAndLineEnd(&content);
+            SkipSpacesAndLineEnd(&content, end);
 
             content = fast_atoreal_move<ai_real>(content, (ai_real &)pLight.mColor.b);
-            SkipSpacesAndLineEnd(&content);
+            SkipSpacesAndLineEnd(&content, end);
         } else if (currentName == "constant_attenuation") {
             XmlParser::getValueAsFloat(currentNode, pLight.mAttConstant);
         } else if (currentName == "linear_attenuation") {
@@ -1220,18 +1225,19 @@ void ColladaParser::ReadEffectColor(XmlNode &node, aiColor4D &pColor, Sampler &p
             std::string v;
             XmlParser::getValueAsString(currentNode, v);
             const char *content = v.c_str();
+            const char *end = v.c_str() + v.size() + 1; 
 
             content = fast_atoreal_move<ai_real>(content, (ai_real &)pColor.r);
-            SkipSpacesAndLineEnd(&content);
+            SkipSpacesAndLineEnd(&content, end);
 
             content = fast_atoreal_move<ai_real>(content, (ai_real &)pColor.g);
-            SkipSpacesAndLineEnd(&content);
+            SkipSpacesAndLineEnd(&content, end);
 
             content = fast_atoreal_move<ai_real>(content, (ai_real &)pColor.b);
-            SkipSpacesAndLineEnd(&content);
+            SkipSpacesAndLineEnd(&content, end);
 
             content = fast_atoreal_move<ai_real>(content, (ai_real &)pColor.a);
-            SkipSpacesAndLineEnd(&content);
+            SkipSpacesAndLineEnd(&content, end);
         } else if (currentName == "texture") {
             // get name of source texture/sampler
             XmlParser::getStdStrAttribute(currentNode, "texture", pSampler.mName);
@@ -1345,6 +1351,7 @@ void ColladaParser::ReadGeometry(XmlNode &node, Collada::Mesh &pMesh) {
     if (node.empty()) {
         return;
     }
+    
     for (XmlNode &currentNode : node.children()) {
         const std::string &currentName = currentNode.name();
         if (currentName == "mesh") {
@@ -1415,6 +1422,7 @@ void ColladaParser::ReadDataArray(XmlNode &node) {
     XmlParser::getValueAsString(node, v);
     v = ai_trim(v);
     const char *content = v.c_str();
+    const char *end = content + v.size();
 
     // read values and store inside an array in the data library
     mDataLibrary[id] = Data();
@@ -1433,11 +1441,13 @@ void ColladaParser::ReadDataArray(XmlNode &node) {
                 }
 
                 s.clear();
-                while (!IsSpaceOrNewLine(*content))
-                    s += *content++;
+                while (!IsSpaceOrNewLine(*content)) {
+                    s += *content;
+                    content++;
+                }
                 data.mStrings.push_back(s);
 
-                SkipSpacesAndLineEnd(&content);
+                SkipSpacesAndLineEnd(&content, end);
             }
         } else {
             data.mValues.reserve(count);
@@ -1452,7 +1462,7 @@ void ColladaParser::ReadDataArray(XmlNode &node) {
                 content = fast_atoreal_move<ai_real>(content, value);
                 data.mValues.push_back(value);
                 // skip whitespace after it
-                SkipSpacesAndLineEnd(&content);
+                SkipSpacesAndLineEnd(&content, end);
             }
         }
     }
@@ -1617,8 +1627,10 @@ void ColladaParser::ReadIndexData(XmlNode &node, Mesh &pMesh) {
                     std::string v;
                     XmlParser::getValueAsString(currentNode, v);
                     const char *content = v.c_str();
+                    const char *end = content + v.size();
+
                     vcount.reserve(numPrimitives);
-                    SkipSpacesAndLineEnd(&content);
+                    SkipSpacesAndLineEnd(&content, end);
                     for (unsigned int a = 0; a < numPrimitives; a++) {
                         if (*content == 0) {
                             throw DeadlyImportError("Expected more values while reading <vcount> contents.");
@@ -1626,7 +1638,7 @@ void ColladaParser::ReadIndexData(XmlNode &node, Mesh &pMesh) {
                         // read a number
                         vcount.push_back((size_t)strtoul10(content, &content));
                         // skip whitespace after it
-                        SkipSpacesAndLineEnd(&content);
+                        SkipSpacesAndLineEnd(&content, end);
                     }
                 }
             }
@@ -1735,14 +1747,16 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector<Inp
         std::string v;
         XmlParser::getValueAsString(node, v);
         const char *content = v.c_str();
-        SkipSpacesAndLineEnd(&content);
+        const char *end = content + v.size();
+
+        SkipSpacesAndLineEnd(&content, end);
         while (*content != 0) {
             // read a value.
             // Hack: (thom) Some exporters put negative indices sometimes. We just try to carry on anyways.
             int value = std::max(0, strtol10(content, &content));
             indices.push_back(size_t(value));
             // skip whitespace after it
-            SkipSpacesAndLineEnd(&content);
+            SkipSpacesAndLineEnd(&content, end);
         }
     }
 
@@ -1801,8 +1815,10 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector<Inp
 
     // For continued primitives, the given count does not come all in one <p>, but only one primitive per <p>
     size_t numPrimitives = pNumPrimitives;
-    if (pPrimType == Prim_TriFans || pPrimType == Prim_Polygon)
+    if (pPrimType == Prim_TriFans || pPrimType == Prim_Polygon) {
         numPrimitives = 1;
+    }
+
     // For continued primitives, the given count is actually the number of <p>'s inside the parent tag
     if (pPrimType == Prim_TriStrips) {
         size_t numberOfVertices = indices.size() / numOffsets;
@@ -2166,15 +2182,15 @@ void ColladaParser::ReadNodeTransformation(XmlNode &node, Node *pNode, Transform
     }
 
     // how many parameters to read per transformation type
-    static const unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 };
+    static constexpr unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 };
     std::string value;
     XmlParser::getValueAsString(node, value);
     const char *content = value.c_str();
-
+    const char *end = value.c_str() + value.size();
     // read as many parameters and store in the transformation
     for (unsigned int a = 0; a < sNumParameters[pType]; a++) {
         // skip whitespace before the number
-        SkipSpacesAndLineEnd(&content);
+        SkipSpacesAndLineEnd(&content, end);
         // read a number
         content = fast_atoreal_move<ai_real>(content, tf.f[a]);
     }

+ 39 - 31
code/AssetLib/Irr/IRRMeshLoader.cpp

@@ -250,7 +250,9 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
             };
 
             // We know what format buffer is, collect numbers
-            ParseBufferVertices(verticesNode.text().get(), vertexFormat,
+            std::string v = verticesNode.text().get();
+            const char *end = v.c_str() + v.size();
+            ParseBufferVertices(v.c_str(), end, vertexFormat,
                     curVertices, curNormals,
                     curTangents, curBitangents,
                     curUVs, curUV2s, curColors, useColors);
@@ -329,8 +331,9 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
 
             // NOTE this might explode for UTF-16 and wchars
             const char *sz = indicesNode.text().get();
+            const char *end = sz + std::strlen(sz) + 1;
             // For each index loop over aiMesh faces
-            while (SkipSpacesAndLineEnd(&sz)) {
+            while (SkipSpacesAndLineEnd(&sz, end)) {
                 if (curFace >= faceEnd) {
                     ASSIMP_LOG_ERROR("IRRMESH: Too many indices");
                     break;
@@ -354,12 +357,18 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
 
                 // Copy over data to aiMesh
                 *pcV++ = curVertices[idx];
-                if (pcN) *pcN++ = curNormals[idx];
-                if (pcT) *pcT++ = curTangents[idx];
-                if (pcB) *pcB++ = curBitangents[idx];
-                if (pcC0) *pcC0++ = curColors[idx];
-                if (pcT0) *pcT0++ = curUVs[idx];
-                if (pcT1) *pcT1++ = curUV2s[idx];
+                if (pcN)
+                    *pcN++ = curNormals[idx];
+                if (pcT)
+                    *pcT++ = curTangents[idx];
+                if (pcB)
+                    *pcB++ = curBitangents[idx];
+                if (pcC0)
+                    *pcC0++ = curColors[idx];
+                if (pcT0)
+                    *pcT0++ = curUVs[idx];
+                if (pcT1)
+                    *pcT1++ = curUV2s[idx];
 
                 // start new face
                 if (++curIdx == 3) {
@@ -421,37 +430,37 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
     };
 }
 
-void IRRMeshImporter::ParseBufferVertices(const char *sz, VertexFormat vertexFormat,
+void IRRMeshImporter::ParseBufferVertices(const char *sz, const char *end, VertexFormat vertexFormat,
         std::vector<aiVector3D> &vertices, std::vector<aiVector3D> &normals,
         std::vector<aiVector3D> &tangents, std::vector<aiVector3D> &bitangents,
         std::vector<aiVector3D> &UVs, std::vector<aiVector3D> &UV2s,
         std::vector<aiColor4D> &colors, bool &useColors) {
     // read vertices
     do {
-        SkipSpacesAndLineEnd(&sz);
+        SkipSpacesAndLineEnd(&sz, end);
         aiVector3D temp;
         aiColor4D c;
 
         // Read the vertex position
         sz = fast_atoreal_move<float>(sz, (float &)temp.x);
-        SkipSpaces(&sz);
+        SkipSpaces(&sz, end);
 
         sz = fast_atoreal_move<float>(sz, (float &)temp.y);
-        SkipSpaces(&sz);
+        SkipSpaces(&sz, end);
 
         sz = fast_atoreal_move<float>(sz, (float &)temp.z);
-        SkipSpaces(&sz);
+        SkipSpaces(&sz, end);
         vertices.push_back(temp);
 
         // Read the vertex normals
         sz = fast_atoreal_move<float>(sz, (float &)temp.x);
-        SkipSpaces(&sz);
+        SkipSpaces(&sz, end);
 
         sz = fast_atoreal_move<float>(sz, (float &)temp.y);
-        SkipSpaces(&sz);
+        SkipSpaces(&sz, end);
 
         sz = fast_atoreal_move<float>(sz, (float &)temp.z);
-        SkipSpaces(&sz);
+        SkipSpaces(&sz, end);
         normals.push_back(temp);
 
         // read the vertex colors
@@ -463,14 +472,14 @@ void IRRMeshImporter::ParseBufferVertices(const char *sz, VertexFormat vertexFor
             useColors = true;
 
         colors.push_back(c);
-        SkipSpaces(&sz);
+        SkipSpaces(&sz, end);
 
         // read the first UV coordinate set
         sz = fast_atoreal_move<float>(sz, (float &)temp.x);
-        SkipSpaces(&sz);
+        SkipSpaces(&sz, end);
 
         sz = fast_atoreal_move<float>(sz, (float &)temp.y);
-        SkipSpaces(&sz);
+        SkipSpaces(&sz, end);
         temp.z = 0.f;
         temp.y = 1.f - temp.y; // DX to OGL
         UVs.push_back(temp);
@@ -480,7 +489,7 @@ void IRRMeshImporter::ParseBufferVertices(const char *sz, VertexFormat vertexFor
         // read the (optional) second UV coordinate set
         if (vertexFormat == VertexFormat::t2coord) {
             sz = fast_atoreal_move<float>(sz, (float &)temp.x);
-            SkipSpaces(&sz);
+            SkipSpaces(&sz, end);
 
             sz = fast_atoreal_move<float>(sz, (float &)temp.y);
             temp.y = 1.f - temp.y; // DX to OGL
@@ -490,33 +499,32 @@ void IRRMeshImporter::ParseBufferVertices(const char *sz, VertexFormat vertexFor
         else if (vertexFormat == VertexFormat::tangent) {
             // tangents
             sz = fast_atoreal_move<float>(sz, (float &)temp.x);
-            SkipSpaces(&sz);
+            SkipSpaces(&sz, end);
 
             sz = fast_atoreal_move<float>(sz, (float &)temp.z);
-            SkipSpaces(&sz);
+            SkipSpaces(&sz, end);
 
             sz = fast_atoreal_move<float>(sz, (float &)temp.y);
-            SkipSpaces(&sz);
+            SkipSpaces(&sz, end);
             temp.y *= -1.0f;
             tangents.push_back(temp);
 
             // bitangents
             sz = fast_atoreal_move<float>(sz, (float &)temp.x);
-            SkipSpaces(&sz);
+            SkipSpaces(&sz, end);
 
             sz = fast_atoreal_move<float>(sz, (float &)temp.z);
-            SkipSpaces(&sz);
+            SkipSpaces(&sz, end);
 
             sz = fast_atoreal_move<float>(sz, (float &)temp.y);
-            SkipSpaces(&sz);
+            SkipSpaces(&sz, end);
             temp.y *= -1.0f;
             bitangents.push_back(temp);
         }
-    } while (SkipLine(&sz));
-    /* IMPORTANT: We assume that each vertex is specified in one
-    line. So we can skip the rest of the line - unknown vertex
-    elements are ignored.
-    */
+    } while (SkipLine(&sz, end));
+    // IMPORTANT: We assume that each vertex is specified in one
+    // line. So we can skip the rest of the line - unknown vertex
+    // elements are ignored.
 }
 
 #endif // !! ASSIMP_BUILD_NO_IRRMESH_IMPORTER

+ 1 - 1
code/AssetLib/Irr/IRRMeshLoader.h

@@ -93,7 +93,7 @@ private:
         tangent = 2, // "tangents" - standard + tangents and bitangents
     };
 
-    void ParseBufferVertices(const char *sz, VertexFormat vertexFormat,
+    void ParseBufferVertices(const char *sz, const char *end, VertexFormat vertexFormat,
             std::vector<aiVector3D> &vertices, std::vector<aiVector3D> &normals,
             std::vector<aiVector3D> &tangents, std::vector<aiVector3D> &bitangents,
             std::vector<aiVector3D> &UVs, std::vector<aiVector3D> &UV2s,

+ 7 - 5
code/AssetLib/Irr/IRRShared.cpp

@@ -135,21 +135,23 @@ void IrrlichtBase::ReadVectorProperty(VectorProperty &out, pugi::xml_node& vecto
         } else if (!ASSIMP_stricmp(attrib.name(), "value")) {
             // three floats, separated with commas
             const char *ptr = attrib.value();
+            size_t len = std::strlen(ptr);
+            const char *end = ptr + len;
 
-            SkipSpaces(&ptr);
+            SkipSpaces(&ptr, end);
             ptr = fast_atoreal_move<float>(ptr, (float &)out.value.x);
-            SkipSpaces(&ptr);
+            SkipSpaces(&ptr, end);
             if (',' != *ptr) {
                 ASSIMP_LOG_ERROR("IRR(MESH): Expected comma in vector definition");
             } else {
-                SkipSpaces(ptr + 1, &ptr);
+                SkipSpaces(ptr + 1, &ptr, end);
             }
             ptr = fast_atoreal_move<float>(ptr, (float &)out.value.y);
-            SkipSpaces(&ptr);
+            SkipSpaces(&ptr, end);
             if (',' != *ptr) {
                 ASSIMP_LOG_ERROR("IRR(MESH): Expected comma in vector definition");
             } else {
-                SkipSpaces(ptr + 1, &ptr);
+                SkipSpaces(ptr + 1, &ptr, end);
             }
             ptr = fast_atoreal_move<float>(ptr, (float &)out.value.z);
         }

+ 1 - 1
code/AssetLib/LWO/LWOBLoader.cpp

@@ -152,7 +152,7 @@ void LWOImporter::CountVertsAndFacesLWOB(unsigned int& verts, unsigned int& face
 }
 
 // ------------------------------------------------------------------------------------------------
-void LWOImporter::CopyFaceIndicesLWOB(FaceList::iterator& it,
+void LWOImporter::CopyFaceIndicesLWOB(FaceList::iterator &it,
     LE_NCONST uint16_t*& cursor,
     const uint16_t* const end,
     unsigned int max) {

+ 77 - 80
code/AssetLib/LWS/LWSLoader.cpp

@@ -78,14 +78,14 @@ static constexpr aiImporterDesc desc = {
 
 // ------------------------------------------------------------------------------------------------
 // Recursive parsing of LWS files
-void LWS::Element::Parse(const char *&buffer) {
-    for (; SkipSpacesAndLineEnd(&buffer); SkipLine(&buffer)) {
+void LWS::Element::Parse(const char *&buffer, const char *end) {
+    for (; SkipSpacesAndLineEnd(&buffer, end); SkipLine(&buffer, end)) {
 
         // begin of a new element with children
         bool sub = false;
         if (*buffer == '{') {
             ++buffer;
-            SkipSpaces(&buffer);
+            SkipSpaces(&buffer, end);
             sub = true;
         } else if (*buffer == '}')
             return;
@@ -98,16 +98,15 @@ void LWS::Element::Parse(const char *&buffer) {
         while (!IsSpaceOrNewLine(*buffer))
             ++buffer;
         children.back().tokens[0] = std::string(cur, (size_t)(buffer - cur));
-        SkipSpaces(&buffer);
+        SkipSpaces(&buffer, end);
 
         if (children.back().tokens[0] == "Plugin") {
             ASSIMP_LOG_VERBOSE_DEBUG("LWS: Skipping over plugin-specific data");
 
             // strange stuff inside Plugin/Endplugin blocks. Needn't
             // follow LWS syntax, so we skip over it
-            for (; SkipSpacesAndLineEnd(&buffer); SkipLine(&buffer)) {
+            for (; SkipSpacesAndLineEnd(&buffer, end); SkipLine(&buffer, end)) {
                 if (!::strncmp(buffer, "EndPlugin", 9)) {
-                    //SkipLine(&buffer);
                     break;
                 }
             }
@@ -122,7 +121,7 @@ void LWS::Element::Parse(const char *&buffer) {
 
         // parse more elements recursively
         if (sub) {
-            children.back().Parse(buffer);
+            children.back().Parse(buffer, end);
         }
     }
 }
@@ -155,7 +154,8 @@ const aiImporterDesc *LWSImporter::GetInfo() const {
     return &desc;
 }
 
-// ------------------------------------------------------------------------------------------------
+static constexpr int MagicHackNo = 150392;
+        // ------------------------------------------------------------------------------------------------
 // Setup configuration properties
 void LWSImporter::SetupProperties(const Importer *pImp) {
     // AI_CONFIG_FAVOUR_SPEED
@@ -163,11 +163,11 @@ void LWSImporter::SetupProperties(const Importer *pImp) {
 
     // AI_CONFIG_IMPORT_LWS_ANIM_START
     first = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWS_ANIM_START,
-            150392 /* magic hack */);
+            MagicHackNo /* magic hack */);
 
     // AI_CONFIG_IMPORT_LWS_ANIM_END
     last = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWS_ANIM_END,
-            150392 /* magic hack */);
+            MagicHackNo /* magic hack */);
 
     if (last < first) {
         std::swap(last, first);
@@ -191,15 +191,16 @@ void LWSImporter::ReadEnvelope(const LWS::Element &dad, LWO::Envelope &fill) {
 
     for (++it; it != dad.children.end(); ++it) {
         const char *c = (*it).tokens[1].c_str();
+        const char *end = c + (*it).tokens[1].size();
 
         if ((*it).tokens[0] == "Key") {
             fill.keys.emplace_back();
             LWO::Key &key = fill.keys.back();
 
             float f;
-            SkipSpaces(&c);
+            SkipSpaces(&c, end);
             c = fast_atoreal_move<float>(c, key.value);
-            SkipSpaces(&c);
+            SkipSpaces(&c, end);
             c = fast_atoreal_move<float>(c, f);
 
             key.time = f;
@@ -231,13 +232,13 @@ void LWSImporter::ReadEnvelope(const LWS::Element &dad, LWO::Envelope &fill) {
                     ASSIMP_LOG_ERROR("LWS: Unknown span type");
             }
             for (unsigned int i = 0; i < num; ++i) {
-                SkipSpaces(&c);
+                SkipSpaces(&c, end);
                 c = fast_atoreal_move<float>(c, key.params[i]);
             }
         } else if ((*it).tokens[0] == "Behaviors") {
-            SkipSpaces(&c);
+            SkipSpaces(&c, end);
             fill.pre = (LWO::PrePostBehaviour)strtoul10(c, &c);
-            SkipSpaces(&c);
+            SkipSpaces(&c, end);
             fill.post = (LWO::PrePostBehaviour)strtoul10(c, &c);
         }
     }
@@ -245,36 +246,39 @@ void LWSImporter::ReadEnvelope(const LWS::Element &dad, LWO::Envelope &fill) {
 
 // ------------------------------------------------------------------------------------------------
 // Read animation channels in the old LightWave animation format
-void LWSImporter::ReadEnvelope_Old(
-        std::list<LWS::Element>::const_iterator &it,
-        const std::list<LWS::Element>::const_iterator &end,
-        LWS::NodeDesc &nodes,
-        unsigned int /*version*/) {
-    unsigned int num, sub_num;
-    if (++it == end) goto unexpected_end;
+void LWSImporter::ReadEnvelope_Old(std::list<LWS::Element>::const_iterator &it,const std::list<LWS::Element>::const_iterator &endIt, 
+        LWS::NodeDesc &nodes, unsigned int) {
+    unsigned int num=0, sub_num=0;
+    if (++it == endIt) {
+        ASSIMP_LOG_ERROR("LWS: Encountered unexpected end of file while parsing object motion");
+        return;
+    }
 
     num = strtoul10((*it).tokens[0].c_str());
     for (unsigned int i = 0; i < num; ++i) {
-
         nodes.channels.emplace_back();
         LWO::Envelope &envl = nodes.channels.back();
 
         envl.index = i;
         envl.type = (LWO::EnvelopeType)(i + 1);
 
-        if (++it == end) {
-            goto unexpected_end;
+        if (++it == endIt) {
+            ASSIMP_LOG_ERROR("LWS: Encountered unexpected end of file while parsing object motion");
+            return;
         }
         sub_num = strtoul10((*it).tokens[0].c_str());
 
         for (unsigned int n = 0; n < sub_num; ++n) {
-
-            if (++it == end) goto unexpected_end;
+            if (++it == endIt) {
+                ASSIMP_LOG_ERROR("LWS: Encountered unexpected end of file while parsing object motion");
+                return;
+            }
 
             // parse value and time, skip the rest for the moment.
             LWO::Key key;
             const char *c = fast_atoreal_move<float>((*it).tokens[0].c_str(), key.value);
-            SkipSpaces(&c);
+            const char *end = c + (*it).tokens[0].size();
+            SkipSpaces(&c, end);
             float f;
             fast_atoreal_move<float>((*it).tokens[0].c_str(), f);
             key.time = f;
@@ -282,10 +286,6 @@ void LWSImporter::ReadEnvelope_Old(
             envl.keys.push_back(key);
         }
     }
-    return;
-
-unexpected_end:
-    ASSIMP_LOG_ERROR("LWS: Encountered unexpected end of file while parsing object motion");
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -296,7 +296,6 @@ void LWSImporter::SetupNodeName(aiNode *nd, LWS::NodeDesc &src) {
     // the name depends on the type. We break LWS's strange naming convention
     // and return human-readable, but still machine-parsable and unique, strings.
     if (src.type == LWS::NodeDesc::OBJECT) {
-
         if (src.path.length()) {
             std::string::size_type s = src.path.find_last_of("\\/");
             if (s == std::string::npos) {
@@ -501,7 +500,8 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
     // Parse the file structure
     LWS::Element root;
     const char *dummy = &mBuffer[0];
-    root.Parse(dummy);
+    const char *dummyEnd = dummy + mBuffer.size();
+    root.Parse(dummy, dummyEnd);
 
     // Construct a Batch-importer to read more files recursively
     BatchLoader batch(pIOHandler);
@@ -540,6 +540,7 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
     // Now read all elements in a very straightforward manner
     for (; it != root.children.end(); ++it) {
         const char *c = (*it).tokens[1].c_str();
+        const char *end = c + (*it).tokens[1].size();
 
         // 'FirstFrame': begin of animation slice
         if ((*it).tokens[0] == "FirstFrame") {
@@ -567,14 +568,14 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
             LWS::NodeDesc d;
             d.type = LWS::NodeDesc::OBJECT;
             if (version >= 4) { // handle LWSC 4 explicit ID
-                SkipSpaces(&c);
+                SkipSpaces(&c, end);
                 d.number = strtoul16(c, &c) & AI_LWS_MASK;
             } else {
                 d.number = cur_object++;
             }
 
             // and add the file to the import list
-            SkipSpaces(&c);
+            SkipSpaces(&c, end);
             std::string path = FindLWOFile(c);
             d.path = path;
             d.id = batch.AddLoadRequest(path, 0, &props);
@@ -588,7 +589,7 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 
             if (version >= 4) { // handle LWSC 4 explicit ID
                 d.number = strtoul16(c, &c) & AI_LWS_MASK;
-                SkipSpaces(&c);
+                SkipSpaces(&c, end);
             } else {
                 d.number = cur_object++;
             }
@@ -604,7 +605,7 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
             d.type = LWS::NodeDesc::OBJECT;
             if (version >= 4) { // handle LWSC 4 explicit ID
                 d.number = strtoul16(c, &c) & AI_LWS_MASK;
-                SkipSpaces(&c);
+                SkipSpaces(&c, end);
             } else {
                 d.number = cur_object++;
             }
@@ -668,26 +669,25 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
                     // two ints per envelope
                     LWO::Envelope &env = *envelopeIt;
                     env.pre = (LWO::PrePostBehaviour)strtoul10(c, &c);
-                    SkipSpaces(&c);
+                    SkipSpaces(&c, end);
                     env.post = (LWO::PrePostBehaviour)strtoul10(c, &c);
-                    SkipSpaces(&c);
+                    SkipSpaces(&c, end);
                 }
             }
         }
         // 'ParentItem': specifies the parent of the current element
         else if ((*it).tokens[0] == "ParentItem") {
-            if (nodes.empty())
+            if (nodes.empty()) {
                 ASSIMP_LOG_ERROR("LWS: Unexpected keyword: \'ParentItem\'");
-
-            else
+            } else {
                 nodes.back().parent = strtoul16(c, &c);
+            }
         }
         // 'ParentObject': deprecated one for older formats
         else if (version < 3 && (*it).tokens[0] == "ParentObject") {
-            if (nodes.empty())
+            if (nodes.empty()) {
                 ASSIMP_LOG_ERROR("LWS: Unexpected keyword: \'ParentObject\'");
-
-            else {
+            } else {
                 nodes.back().parent = strtoul10(c, &c) | (1u << 28u);
             }
         }
@@ -700,19 +700,20 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 
             if (version >= 4) { // handle LWSC 4 explicit ID
                 d.number = strtoul16(c, &c) & AI_LWS_MASK;
-            } else
+            } else {
                 d.number = cur_camera++;
+            }
             nodes.push_back(d);
 
             num_camera++;
         }
         // 'CameraName': set name of currently active camera
         else if ((*it).tokens[0] == "CameraName") {
-            if (nodes.empty() || nodes.back().type != LWS::NodeDesc::CAMERA)
+            if (nodes.empty() || nodes.back().type != LWS::NodeDesc::CAMERA) {
                 ASSIMP_LOG_ERROR("LWS: Unexpected keyword: \'CameraName\'");
-
-            else
+            } else {
                 nodes.back().name = c;
+            }
         }
         // 'AddLight': add a light to the scenegraph
         else if ((*it).tokens[0] == "AddLight") {
@@ -723,19 +724,20 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 
             if (version >= 4) { // handle LWSC 4 explicit ID
                 d.number = strtoul16(c, &c) & AI_LWS_MASK;
-            } else
+            } else {
                 d.number = cur_light++;
+            }
             nodes.push_back(d);
 
             num_light++;
         }
         // 'LightName': set name of currently active light
         else if ((*it).tokens[0] == "LightName") {
-            if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+            if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) {
                 ASSIMP_LOG_ERROR("LWS: Unexpected keyword: \'LightName\'");
-
-            else
+            } else {
                 nodes.back().name = c;
+            }
         }
         // 'LightIntensity': set intensity of currently active light
         else if ((*it).tokens[0] == "LightIntensity" || (*it).tokens[0] == "LgtIntensity") {
@@ -753,62 +755,58 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
         }
         // 'LightType': set type of currently active light
         else if ((*it).tokens[0] == "LightType") {
-            if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+            if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) {
                 ASSIMP_LOG_ERROR("LWS: Unexpected keyword: \'LightType\'");
-
-            else
+            } else {
                 nodes.back().lightType = strtoul10(c);
-
+            }
         }
         // 'LightFalloffType': set falloff type of currently active light
         else if ((*it).tokens[0] == "LightFalloffType") {
-            if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+            if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) {
                 ASSIMP_LOG_ERROR("LWS: Unexpected keyword: \'LightFalloffType\'");
-            else
+            } else {
                 nodes.back().lightFalloffType = strtoul10(c);
-
+            }
         }
         // 'LightConeAngle': set cone angle of currently active light
         else if ((*it).tokens[0] == "LightConeAngle") {
-            if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+            if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) {
                 ASSIMP_LOG_ERROR("LWS: Unexpected keyword: \'LightConeAngle\'");
-
-            else
+            } else {
                 nodes.back().lightConeAngle = fast_atof(c);
-
+            }
         }
         // 'LightEdgeAngle': set area where we're smoothing from min to max intensity
         else if ((*it).tokens[0] == "LightEdgeAngle") {
-            if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+            if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) {
                 ASSIMP_LOG_ERROR("LWS: Unexpected keyword: \'LightEdgeAngle\'");
-
-            else
+            } else {
                 nodes.back().lightEdgeAngle = fast_atof(c);
-
+            }
         }
         // 'LightColor': set color of currently active light
         else if ((*it).tokens[0] == "LightColor") {
-            if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+            if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) {
                 ASSIMP_LOG_ERROR("LWS: Unexpected keyword: \'LightColor\'");
-
-            else {
+            } else {
                 c = fast_atoreal_move<float>(c, (float &)nodes.back().lightColor.r);
-                SkipSpaces(&c);
+                SkipSpaces(&c, end);
                 c = fast_atoreal_move<float>(c, (float &)nodes.back().lightColor.g);
-                SkipSpaces(&c);
+                SkipSpaces(&c, end);
                 c = fast_atoreal_move<float>(c, (float &)nodes.back().lightColor.b);
             }
         }
 
         // 'PivotPosition': position of local transformation origin
         else if ((*it).tokens[0] == "PivotPosition" || (*it).tokens[0] == "PivotPoint") {
-            if (nodes.empty())
+            if (nodes.empty()) {
                 ASSIMP_LOG_ERROR("LWS: Unexpected keyword: \'PivotPosition\'");
-            else {
+            } else {
                 c = fast_atoreal_move<float>(c, (float &)nodes.back().pivotPos.x);
-                SkipSpaces(&c);
+                SkipSpaces(&c, end);
                 c = fast_atoreal_move<float>(c, (float &)nodes.back().pivotPos.y);
-                SkipSpaces(&c);
+                SkipSpaces(&c, end);
                 c = fast_atoreal_move<float>(c, (float &)nodes.back().pivotPos.z);
                 // Mark pivotPos as set
                 nodes.back().isPivotSet = true;
@@ -818,7 +816,6 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 
     // resolve parenting
     for (std::list<LWS::NodeDesc>::iterator ndIt = nodes.begin(); ndIt != nodes.end(); ++ndIt) {
-
         // check whether there is another node which calls us a parent
         for (std::list<LWS::NodeDesc>::iterator dit = nodes.begin(); dit != nodes.end(); ++dit) {
             if (dit != ndIt && *ndIt == (*dit).parent) {
@@ -854,7 +851,7 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
     aiNode *nd = master->mRootNode = new aiNode();
 
     // allocate storage for cameras&lights
-    if (num_camera) {
+    if (num_camera > 0u) {
         master->mCameras = new aiCamera *[master->mNumCameras = num_camera];
     }
     aiCamera **cams = master->mCameras;

+ 1 - 1
code/AssetLib/LWS/LWSLoader.h

@@ -76,7 +76,7 @@ public:
     std::list<Element> children;
 
     //! Recursive parsing function
-    void Parse(const char *&buffer);
+    void Parse(const char *&buffer, const char *end);
 };
 
 #define AI_LWS_MASK (0xffffffff >> 4u)

+ 17 - 15
code/AssetLib/MD3/MD3Loader.cpp

@@ -123,12 +123,12 @@ bool Q3Shader::LoadShader(ShaderData &fill, const std::string &pFile, IOSystem *
     // remove comments from it (C++ style)
     CommentRemover::RemoveLineComments("//", &_buff[0]);
     const char *buff = &_buff[0];
-
+    const char *end = buff + _buff.size();
     Q3Shader::ShaderDataBlock *curData = nullptr;
     Q3Shader::ShaderMapBlock *curMap = nullptr;
 
     // read line per line
-    for (; SkipSpacesAndLineEnd(&buff); SkipLine(&buff)) {
+    for (; SkipSpacesAndLineEnd(&buff, end); SkipLine(&buff, end)) {
 
         if (*buff == '{') {
             ++buff;
@@ -140,21 +140,21 @@ bool Q3Shader::LoadShader(ShaderData &fill, const std::string &pFile, IOSystem *
             }
 
             // read this data section
-            for (; SkipSpacesAndLineEnd(&buff); SkipLine(&buff)) {
+            for (; SkipSpacesAndLineEnd(&buff, end); SkipLine(&buff, end)) {
                 if (*buff == '{') {
                     ++buff;
                     // add new map section
                     curData->maps.emplace_back();
                     curMap = &curData->maps.back();
 
-                    for (; SkipSpacesAndLineEnd(&buff); SkipLine(&buff)) {
+                    for (; SkipSpacesAndLineEnd(&buff, end); SkipLine(&buff, end)) {
                         // 'map' - Specifies texture file name
                         if (TokenMatchI(buff, "map", 3) || TokenMatchI(buff, "clampmap", 8)) {
-                            curMap->name = GetNextToken(buff);
+                            curMap->name = GetNextToken(buff, end);
                         }
                         // 'blendfunc' - Alpha blending mode
                         else if (TokenMatchI(buff, "blendfunc", 9)) {
-                            const std::string blend_src = GetNextToken(buff);
+                            const std::string blend_src = GetNextToken(buff, end);
                             if (blend_src == "add") {
                                 curMap->blend_src = Q3Shader::BLEND_GL_ONE;
                                 curMap->blend_dest = Q3Shader::BLEND_GL_ONE;
@@ -166,12 +166,12 @@ bool Q3Shader::LoadShader(ShaderData &fill, const std::string &pFile, IOSystem *
                                 curMap->blend_dest = Q3Shader::BLEND_GL_ONE_MINUS_SRC_ALPHA;
                             } else {
                                 curMap->blend_src = StringToBlendFunc(blend_src);
-                                curMap->blend_dest = StringToBlendFunc(GetNextToken(buff));
+                                curMap->blend_dest = StringToBlendFunc(GetNextToken(buff, end));
                             }
                         }
                         // 'alphafunc' - Alpha testing mode
                         else if (TokenMatchI(buff, "alphafunc", 9)) {
-                            const std::string at = GetNextToken(buff);
+                            const std::string at = GetNextToken(buff, end);
                             if (at == "GT0") {
                                 curMap->alpha_test = Q3Shader::AT_GT0;
                             } else if (at == "LT128") {
@@ -186,7 +186,6 @@ bool Q3Shader::LoadShader(ShaderData &fill, const std::string &pFile, IOSystem *
                             break;
                         }
                     }
-
                 } else if (*buff == '}') {
                     ++buff;
                     curData = nullptr;
@@ -195,7 +194,7 @@ bool Q3Shader::LoadShader(ShaderData &fill, const std::string &pFile, IOSystem *
 
                 // 'cull' specifies culling behaviour for the model
                 else if (TokenMatchI(buff, "cull", 4)) {
-                    SkipSpaces(&buff);
+                    SkipSpaces(&buff, end);
                     if (!ASSIMP_strincmp(buff, "back", 4)) { // render face's backside, does not function in Q3 engine (bug)
                         curData->cull = Q3Shader::CULL_CCW;
                     } else if (!ASSIMP_strincmp(buff, "front", 5)) { // is not valid keyword in Q3, but occurs in shaders
@@ -213,9 +212,10 @@ bool Q3Shader::LoadShader(ShaderData &fill, const std::string &pFile, IOSystem *
             curData = &fill.blocks.back();
 
             // get the name of this section
-            curData->name = GetNextToken(buff);
+            curData->name = GetNextToken(buff, end);
         }
     }
+
     return true;
 }
 
@@ -232,6 +232,7 @@ bool Q3Shader::LoadSkin(SkinData &fill, const std::string &pFile, IOSystem *io)
     const size_t s = file->FileSize();
     std::vector<char> _buff(s + 1);
     const char *buff = &_buff[0];
+    const char *end = buff + _buff.size();
     file->Read(&_buff[0], s, 1);
     _buff[s] = 0;
 
@@ -240,10 +241,10 @@ bool Q3Shader::LoadSkin(SkinData &fill, const std::string &pFile, IOSystem *io)
 
     // read token by token and fill output table
     for (; *buff;) {
-        SkipSpacesAndLineEnd(&buff);
+        SkipSpacesAndLineEnd(&buff, end);
 
         // get first identifier
-        std::string ss = GetNextToken(buff);
+        std::string ss = GetNextToken(buff, end);
 
         // ignore tokens starting with tag_
         if (!::strncmp(&ss[0], "tag_", std::min((size_t)4, ss.length())))
@@ -253,8 +254,9 @@ bool Q3Shader::LoadSkin(SkinData &fill, const std::string &pFile, IOSystem *io)
         SkinData::TextureEntry &entry = fill.textures.back();
 
         entry.first = ss;
-        entry.second = GetNextToken(buff);
+        entry.second = GetNextToken(buff, end);
     }
+
     return true;
 }
 
@@ -293,7 +295,7 @@ void Q3Shader::ConvertShaderToMaterial(aiMaterial *out, const ShaderDataBlock &s
         //  - in any case: set it as diffuse texture
         //
         // If the texture is using 'filter' blending
-        //  - take as lightmap
+        //  - take as light-map
         //
         // Textures with alpha funcs
         //  - aiTextureFlags_UseAlpha is set (otherwise aiTextureFlags_NoAlpha is explicitly set)

+ 8 - 8
code/AssetLib/MD5/MD5Loader.cpp

@@ -210,7 +210,7 @@ void MD5Importer::MakeDataUnique(MD5::MeshDesc &meshSrc) {
     const unsigned int guess = (unsigned int)(fWeightsPerVert * iNewNum);
     meshSrc.mWeights.reserve(guess + (guess >> 3)); // + 12.5% as buffer
 
-    for (FaceList::const_iterator iter = meshSrc.mFaces.begin(), iterEnd = meshSrc.mFaces.end(); iter != iterEnd; ++iter) {
+    for (FaceArray::const_iterator iter = meshSrc.mFaces.begin(), iterEnd = meshSrc.mFaces.end(); iter != iterEnd; ++iter) {
         const aiFace &face = *iter;
         for (unsigned int i = 0; i < 3; ++i) {
             if (face.mIndices[0] >= meshSrc.mVertices.size()) {
@@ -231,7 +231,7 @@ void MD5Importer::MakeDataUnique(MD5::MeshDesc &meshSrc) {
 
 // ------------------------------------------------------------------------------------------------
 // Recursive node graph construction from a MD5MESH
-void MD5Importer::AttachChilds_Mesh(int iParentID, aiNode *piParent, BoneList &bones) {
+void MD5Importer::AttachChilds_Mesh(int iParentID, aiNode *piParent, BoneArray &bones) {
     ai_assert(nullptr != piParent);
     ai_assert(!piParent->mNumChildren);
 
@@ -282,7 +282,7 @@ void MD5Importer::AttachChilds_Mesh(int iParentID, aiNode *piParent, BoneList &b
 
 // ------------------------------------------------------------------------------------------------
 // Recursive node graph construction from a MD5ANIM
-void MD5Importer::AttachChilds_Anim(int iParentID, aiNode *piParent, AnimBoneList &bones, const aiNodeAnim **node_anims) {
+void MD5Importer::AttachChilds_Anim(int iParentID, aiNode *piParent, AnimBoneArray &bones, const aiNodeAnim **node_anims) {
     ai_assert(nullptr != piParent);
     ai_assert(!piParent->mNumChildren);
 
@@ -402,7 +402,7 @@ void MD5Importer::LoadMD5MeshFile() {
 
         // copy texture coordinates
         aiVector3D *pv = mesh->mTextureCoords[0];
-        for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin(); iter != meshSrc.mVertices.end(); ++iter, ++pv) {
+        for (MD5::VertexArray::const_iterator iter = meshSrc.mVertices.begin(); iter != meshSrc.mVertices.end(); ++iter, ++pv) {
             pv->x = (*iter).mUV.x;
             pv->y = 1.0f - (*iter).mUV.y; // D3D to OpenGL
             pv->z = 0.0f;
@@ -412,7 +412,7 @@ void MD5Importer::LoadMD5MeshFile() {
         unsigned int *piCount = new unsigned int[meshParser.mJoints.size()];
         ::memset(piCount, 0, sizeof(unsigned int) * meshParser.mJoints.size());
 
-        for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin(); iter != meshSrc.mVertices.end(); ++iter, ++pv) {
+        for (MD5::VertexArray::const_iterator iter = meshSrc.mVertices.begin(); iter != meshSrc.mVertices.end(); ++iter, ++pv) {
             for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights; ++w) {
                 MD5::WeightDesc &weightDesc = meshSrc.mWeights[w];
                 /* FIX for some invalid exporters */
@@ -447,7 +447,7 @@ void MD5Importer::LoadMD5MeshFile() {
             }
 
             pv = mesh->mVertices;
-            for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin(); iter != meshSrc.mVertices.end(); ++iter, ++pv) {
+            for (MD5::VertexArray::const_iterator iter = meshSrc.mVertices.begin(); iter != meshSrc.mVertices.end(); ++iter, ++pv) {
                 // compute the final vertex position from all single weights
                 *pv = aiVector3D();
 
@@ -585,14 +585,14 @@ void MD5Importer::LoadMD5AnimFile() {
         // 1 tick == 1 frame
         anim->mTicksPerSecond = animParser.fFrameRate;
 
-        for (FrameList::const_iterator iter = animParser.mFrames.begin(), iterEnd = animParser.mFrames.end(); iter != iterEnd; ++iter) {
+        for (FrameArray::const_iterator iter = animParser.mFrames.begin(), iterEnd = animParser.mFrames.end(); iter != iterEnd; ++iter) {
             double dTime = (double)(*iter).iIndex;
             aiNodeAnim **pcAnimNode = anim->mChannels;
             if (!(*iter).mValues.empty() || iter == animParser.mFrames.begin()) /* be sure we have at least one frame */
             {
                 // now process all values in there ... read all joints
                 MD5::BaseFrameDesc *pcBaseFrame = &animParser.mBaseFrames[0];
-                for (AnimBoneList::const_iterator iter2 = animParser.mAnimatedBones.begin(); iter2 != animParser.mAnimatedBones.end(); ++iter2,
+                for (AnimBoneArray::const_iterator iter2 = animParser.mAnimatedBones.begin(); iter2 != animParser.mAnimatedBones.end(); ++iter2,
                                                   ++pcAnimNode, ++pcBaseFrame) {
                     if ((*iter2).iFirstKeyIndex >= (*iter).mValues.size()) {
 

+ 3 - 3
code/AssetLib/MD5/MD5Loader.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2022, assimp team
+Copyright (c) 2006-2024, assimp team
 
 All rights reserved.
 
@@ -118,7 +118,7 @@ protected:
      *  @param node_anims Generated node animations
     */
     void AttachChilds_Anim(int iParentID, aiNode *piParent,
-            AnimBoneList &bones, const aiNodeAnim **node_anims);
+            AnimBoneArray &bones, const aiNodeAnim **node_anims);
 
     // -------------------------------------------------------------------
     /** Construct node hierarchy from a given MD5MESH
@@ -126,7 +126,7 @@ protected:
      *  @param piParent Parent node to attach to
      *  @param bones Input bones
     */
-    void AttachChilds_Mesh(int iParentID, aiNode *piParent, BoneList &bones);
+    void AttachChilds_Mesh(int iParentID, aiNode *piParent, BoneArray &bones);
 
     // -------------------------------------------------------------------
     /** Build unique vertex buffers from a given MD5ANIM

+ 124 - 98
code/AssetLib/MD5/MD5Parser.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2023, assimp team
+Copyright (c) 2006-2024, assimp team
 
 All rights reserved.
 
@@ -138,14 +138,16 @@ bool MD5Parser::ParseSection(Section &out) {
     char *sz = buffer;
     while (!IsSpaceOrNewLine(*buffer)) {
         ++buffer;
-        if (buffer == bufferEnd)
+        if (buffer == bufferEnd) {
             return false;
+	    }
     }
     out.mName = std::string(sz, (uintptr_t)(buffer - sz));
     while (IsSpace(*buffer)) {
         ++buffer;
-        if (buffer == bufferEnd)
+        if (buffer == bufferEnd) {
             return false;
+	    }
     }
 
     bool running = true;
@@ -153,14 +155,16 @@ bool MD5Parser::ParseSection(Section &out) {
         if ('{' == *buffer) {
             // it is a normal section so read all lines
             ++buffer;
-            if (buffer == bufferEnd)
+            if (buffer == bufferEnd) {
                 return false;
+	        }
             bool run = true;
             while (run) {
                 while (IsSpaceOrNewLine(*buffer)) {
                     ++buffer;
-                    if (buffer == bufferEnd)
+                    if (buffer == bufferEnd) {
                         return false;
+		            }
                 }
                 if ('\0' == *buffer) {
                     return false; // seems this was the last section
@@ -175,18 +179,21 @@ bool MD5Parser::ParseSection(Section &out) {
 
                 elem.iLineNumber = lineNumber;
                 elem.szStart = buffer;
+                elem.end = bufferEnd;
 
                 // terminate the line with zero
                 while (!IsLineEnd(*buffer)) {
                     ++buffer;
-                    if (buffer == bufferEnd)
+                    if (buffer == bufferEnd) {
                         return false;
+		            }
                 }
                 if (*buffer) {
                     ++lineNumber;
                     *buffer++ = '\0';
-                    if (buffer == bufferEnd)
+                    if (buffer == bufferEnd) {
                         return false;
+		            }
                 }
             }
             break;
@@ -194,89 +201,107 @@ bool MD5Parser::ParseSection(Section &out) {
             // it is an element at global scope. Parse its value and go on
             sz = buffer;
             while (!IsSpaceOrNewLine(*buffer++)) {
-                if (buffer == bufferEnd)
+                if (buffer == bufferEnd) {
                     return false;
+		        }
             }
             out.mGlobalValue = std::string(sz, (uintptr_t)(buffer - sz));
             continue;
         }
         break;
     }
-    if (buffer == bufferEnd)
+    if (buffer == bufferEnd) {
         return false;
+    }
     while (IsSpaceOrNewLine(*buffer)) {
+        if (buffer == bufferEnd) {
+            break;
+	    }
         ++buffer;
-        if (buffer == bufferEnd)
-            return false;
     }
     return '\0' != *buffer;
 }
 
-// ------------------------------------------------------------------------------------------------
-// Some dirty macros just because they're so funny and easy to debug
-
 // skip all spaces ... handle EOL correctly
-#define AI_MD5_SKIP_SPACES() \
-    if (!SkipSpaces(&sz))    \
-        MD5Parser::ReportWarning("Unexpected end of line", elem.iLineNumber);
+inline void AI_MD5_SKIP_SPACES(const char **sz, const char *bufferEnd, int linenumber) {
+    if (!SkipSpaces(sz, bufferEnd)) {
+        MD5Parser::ReportWarning("Unexpected end of line", linenumber);
+    }
+}
 
 // read a triple float in brackets: (1.0 1.0 1.0)
-#define AI_MD5_READ_TRIPLE(vec)                                                         \
-    AI_MD5_SKIP_SPACES();                                                               \
-    if ('(' != *sz++)                                                                   \
-        MD5Parser::ReportWarning("Unexpected token: ( was expected", elem.iLineNumber); \
-    AI_MD5_SKIP_SPACES();                                                               \
-    sz = fast_atoreal_move<float>(sz, (float &)vec.x);                                  \
-    AI_MD5_SKIP_SPACES();                                                               \
-    sz = fast_atoreal_move<float>(sz, (float &)vec.y);                                  \
-    AI_MD5_SKIP_SPACES();                                                               \
-    sz = fast_atoreal_move<float>(sz, (float &)vec.z);                                  \
-    AI_MD5_SKIP_SPACES();                                                               \
-    if (')' != *sz++)                                                                   \
-        MD5Parser::ReportWarning("Unexpected token: ) was expected", elem.iLineNumber);
+inline void AI_MD5_READ_TRIPLE(aiVector3D &vec, const char **sz, const char *bufferEnd, int linenumber) {
+    AI_MD5_SKIP_SPACES(sz, bufferEnd, linenumber);
+    if ('(' != **sz) {
+        MD5Parser::ReportWarning("Unexpected token: ( was expected", linenumber);
+        ++*sz;
+    }
+    ++*sz;
+    AI_MD5_SKIP_SPACES(sz, bufferEnd, linenumber);
+    *sz = fast_atoreal_move<float>(*sz, (float &)vec.x);
+    AI_MD5_SKIP_SPACES(sz, bufferEnd, linenumber);
+    *sz = fast_atoreal_move<float>(*sz, (float &)vec.y);
+    AI_MD5_SKIP_SPACES(sz, bufferEnd, linenumber);
+    *sz = fast_atoreal_move<float>(*sz, (float &)vec.z);
+    AI_MD5_SKIP_SPACES(sz, bufferEnd, linenumber);
+    if (')' != **sz) {
+        MD5Parser::ReportWarning("Unexpected token: ) was expected", linenumber);
+    }
+    ++*sz;
+}
 
 // parse a string, enclosed in quotation marks or not
-#define AI_MD5_PARSE_STRING(out)                                                   \
-    bool bQuota = (*sz == '\"');                                                   \
-    const char *szStart = sz;                                                      \
-    while (!IsSpaceOrNewLine(*sz))                                                 \
-        ++sz;                                                                      \
-    const char *szEnd = sz;                                                        \
-    if (bQuota) {                                                                  \
-        szStart++;                                                                 \
-        if ('\"' != *(szEnd -= 1)) {                                               \
-            MD5Parser::ReportWarning("Expected closing quotation marks in string", \
-                    elem.iLineNumber);                                             \
-            continue;                                                              \
-        }                                                                          \
-    }                                                                              \
-    out.length = (size_t)(szEnd - szStart);                                        \
-    ::memcpy(out.data, szStart, out.length);                                       \
+inline bool AI_MD5_PARSE_STRING(const char **sz, const char *bufferEnd, aiString &out, int linenumber) {
+    bool bQuota = (**sz == '\"');
+    const char *szStart = *sz;
+    while (!IsSpaceOrNewLine(**sz)) {
+        ++*sz;
+        if (*sz == bufferEnd) break;
+    }
+    const char *szEnd = *sz;
+    if (bQuota) {
+        szStart++;
+        if ('\"' != *(szEnd -= 1)) {
+            MD5Parser::ReportWarning("Expected closing quotation marks in string", linenumber);
+            ++*sz;
+        }
+    }
+    out.length = (ai_uint32)(szEnd - szStart);
+    ::memcpy(out.data, szStart, out.length);
     out.data[out.length] = '\0';
 
+    return true;
+}
+
 // parse a string, enclosed in quotation marks
-#define AI_MD5_PARSE_STRING_IN_QUOTATION(out)          \
-    out.length = 0;                                    \
-    while ('\"' != *sz && '\0' != *sz)                 \
-        ++sz;                                          \
-    if ('\0' != *sz) {                                 \
-        const char *szStart = ++sz;                    \
-        while ('\"' != *sz && '\0' != *sz)             \
-            ++sz;                                      \
-        if ('\0' != *sz) {                             \
-            const char *szEnd = (sz++);                \
-            out.length = (ai_uint32)(szEnd - szStart); \
-            ::memcpy(out.data, szStart, out.length);   \
-        }                                              \
-    }                                                  \
+inline void AI_MD5_PARSE_STRING_IN_QUOTATION(const char **sz, const char *bufferEnd, aiString &out) {
+    out.length = 0u;
+    while (('\"' != **sz && '\0' != **sz) && *sz != bufferEnd) {
+        ++*sz;
+    }
+    if ('\0' != **sz) {
+        const char *szStart = ++(*sz);
+        
+        while (('\"' != **sz && '\0' != **sz) && *sz != bufferEnd) {
+            ++*sz;
+        }
+        if ('\0' != **sz) {
+            const char *szEnd = *sz;
+            ++*sz;
+            out.length = (ai_uint32)(szEnd - szStart);
+            ::memcpy(out.data, szStart, out.length);
+        }
+    }
     out.data[out.length] = '\0';
+}
+
 // ------------------------------------------------------------------------------------------------
 // .MD5MESH parsing function
-MD5MeshParser::MD5MeshParser(SectionList &mSections) {
+MD5MeshParser::MD5MeshParser(SectionArray &mSections) {
     ASSIMP_LOG_DEBUG("MD5MeshParser begin");
 
     // now parse all sections
-    for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end(); iter != iterEnd; ++iter) {
+    for (SectionArray::const_iterator iter = mSections.begin(), iterEnd = mSections.end(); iter != iterEnd; ++iter) {
         if ((*iter).mName == "numMeshes") {
             mMeshes.reserve(::strtoul10((*iter).mGlobalValue.c_str()));
         } else if ((*iter).mName == "numJoints") {
@@ -288,14 +313,15 @@ MD5MeshParser::MD5MeshParser(SectionList &mSections) {
                 BoneDesc &desc = mJoints.back();
 
                 const char *sz = elem.szStart;
-                AI_MD5_PARSE_STRING_IN_QUOTATION(desc.mName);
-                AI_MD5_SKIP_SPACES();
+                AI_MD5_PARSE_STRING_IN_QUOTATION(&sz, elem.end, desc.mName);
+                
+                AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
 
                 // negative values, at least -1, is allowed here
                 desc.mParentIndex = (int)strtol10(sz, &sz);
 
-                AI_MD5_READ_TRIPLE(desc.mPositionXYZ);
-                AI_MD5_READ_TRIPLE(desc.mRotationQuat); // normalized quaternion, so w is not there
+                AI_MD5_READ_TRIPLE(desc.mPositionXYZ, &sz, elem.end, elem.iLineNumber);
+                AI_MD5_READ_TRIPLE(desc.mRotationQuat, &sz, elem.end, elem.iLineNumber); // normalized quaternion, so w is not there
             }
         } else if ((*iter).mName == "mesh") {
             mMeshes.emplace_back();
@@ -306,52 +332,52 @@ MD5MeshParser::MD5MeshParser(SectionList &mSections) {
 
                 // shader attribute
                 if (TokenMatch(sz, "shader", 6)) {
-                    AI_MD5_SKIP_SPACES();
-                    AI_MD5_PARSE_STRING_IN_QUOTATION(desc.mShader);
+                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
+                    AI_MD5_PARSE_STRING_IN_QUOTATION(&sz, elem.end, desc.mShader);
                 }
                 // numverts attribute
                 else if (TokenMatch(sz, "numverts", 8)) {
-                    AI_MD5_SKIP_SPACES();
+                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
                     desc.mVertices.resize(strtoul10(sz));
                 }
                 // numtris attribute
                 else if (TokenMatch(sz, "numtris", 7)) {
-                    AI_MD5_SKIP_SPACES();
+                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
                     desc.mFaces.resize(strtoul10(sz));
                 }
                 // numweights attribute
                 else if (TokenMatch(sz, "numweights", 10)) {
-                    AI_MD5_SKIP_SPACES();
+                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
                     desc.mWeights.resize(strtoul10(sz));
                 }
                 // vert attribute
                 // "vert 0 ( 0.394531 0.513672 ) 0 1"
                 else if (TokenMatch(sz, "vert", 4)) {
-                    AI_MD5_SKIP_SPACES();
+                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
                     const unsigned int idx = ::strtoul10(sz, &sz);
-                    AI_MD5_SKIP_SPACES();
+                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
                     if (idx >= desc.mVertices.size())
                         desc.mVertices.resize(idx + 1);
 
                     VertexDesc &vert = desc.mVertices[idx];
                     if ('(' != *sz++)
                         MD5Parser::ReportWarning("Unexpected token: ( was expected", elem.iLineNumber);
-                    AI_MD5_SKIP_SPACES();
+                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
                     sz = fast_atoreal_move<float>(sz, (float &)vert.mUV.x);
-                    AI_MD5_SKIP_SPACES();
+                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
                     sz = fast_atoreal_move<float>(sz, (float &)vert.mUV.y);
-                    AI_MD5_SKIP_SPACES();
+                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
                     if (')' != *sz++)
                         MD5Parser::ReportWarning("Unexpected token: ) was expected", elem.iLineNumber);
-                    AI_MD5_SKIP_SPACES();
+                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
                     vert.mFirstWeight = ::strtoul10(sz, &sz);
-                    AI_MD5_SKIP_SPACES();
+                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
                     vert.mNumWeights = ::strtoul10(sz, &sz);
                 }
                 // tri attribute
                 // "tri 0 15 13 12"
                 else if (TokenMatch(sz, "tri", 3)) {
-                    AI_MD5_SKIP_SPACES();
+                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
                     const unsigned int idx = strtoul10(sz, &sz);
                     if (idx >= desc.mFaces.size())
                         desc.mFaces.resize(idx + 1);
@@ -359,24 +385,24 @@ MD5MeshParser::MD5MeshParser(SectionList &mSections) {
                     aiFace &face = desc.mFaces[idx];
                     face.mIndices = new unsigned int[face.mNumIndices = 3];
                     for (unsigned int i = 0; i < 3; ++i) {
-                        AI_MD5_SKIP_SPACES();
+                        AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
                         face.mIndices[i] = strtoul10(sz, &sz);
                     }
                 }
                 // weight attribute
                 // "weight 362 5 0.500000 ( -3.553583 11.893474 9.719339 )"
                 else if (TokenMatch(sz, "weight", 6)) {
-                    AI_MD5_SKIP_SPACES();
+                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
                     const unsigned int idx = strtoul10(sz, &sz);
-                    AI_MD5_SKIP_SPACES();
+                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
                     if (idx >= desc.mWeights.size())
                         desc.mWeights.resize(idx + 1);
 
                     WeightDesc &weight = desc.mWeights[idx];
                     weight.mBone = strtoul10(sz, &sz);
-                    AI_MD5_SKIP_SPACES();
+                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
                     sz = fast_atoreal_move<float>(sz, weight.mWeight);
-                    AI_MD5_READ_TRIPLE(weight.vOffsetPosition);
+                    AI_MD5_READ_TRIPLE(weight.vOffsetPosition, &sz, elem.end, elem.iLineNumber);
                 }
             }
         }
@@ -386,12 +412,12 @@ MD5MeshParser::MD5MeshParser(SectionList &mSections) {
 
 // ------------------------------------------------------------------------------------------------
 // .MD5ANIM parsing function
-MD5AnimParser::MD5AnimParser(SectionList &mSections) {
+MD5AnimParser::MD5AnimParser(SectionArray &mSections) {
     ASSIMP_LOG_DEBUG("MD5AnimParser begin");
 
     fFrameRate = 24.0f;
     mNumAnimatedComponents = UINT_MAX;
-    for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end(); iter != iterEnd; ++iter) {
+    for (SectionArray::const_iterator iter = mSections.begin(), iterEnd = mSections.end(); iter != iterEnd; ++iter) {
         if ((*iter).mName == "hierarchy") {
             // "sheath" 0 63 6
             for (const auto &elem : (*iter).mElements) {
@@ -399,18 +425,18 @@ MD5AnimParser::MD5AnimParser(SectionList &mSections) {
                 AnimBoneDesc &desc = mAnimatedBones.back();
 
                 const char *sz = elem.szStart;
-                AI_MD5_PARSE_STRING_IN_QUOTATION(desc.mName);
-                AI_MD5_SKIP_SPACES();
+                AI_MD5_PARSE_STRING_IN_QUOTATION(&sz, elem.end, desc.mName);
+                AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
 
                 // parent index - negative values are allowed (at least -1)
                 desc.mParentIndex = ::strtol10(sz, &sz);
 
                 // flags (highest is 2^6-1)
-                AI_MD5_SKIP_SPACES();
+                AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
                 if (63 < (desc.iFlags = ::strtoul10(sz, &sz))) {
                     MD5Parser::ReportWarning("Invalid flag combination in hierarchy section", elem.iLineNumber);
                 }
-                AI_MD5_SKIP_SPACES();
+                AI_MD5_SKIP_SPACES(&  sz, elem.end, elem.iLineNumber);
 
                 // index of the first animation keyframe component for this joint
                 desc.iFirstKeyIndex = ::strtoul10(sz, &sz);
@@ -423,8 +449,8 @@ MD5AnimParser::MD5AnimParser(SectionList &mSections) {
                 mBaseFrames.emplace_back();
                 BaseFrameDesc &desc = mBaseFrames.back();
 
-                AI_MD5_READ_TRIPLE(desc.vPositionXYZ);
-                AI_MD5_READ_TRIPLE(desc.vRotationQuat);
+                AI_MD5_READ_TRIPLE(desc.vPositionXYZ, &sz, elem.end, elem.iLineNumber);
+                AI_MD5_READ_TRIPLE(desc.vRotationQuat, &sz, elem.end, elem.iLineNumber);
             }
         } else if ((*iter).mName == "frame") {
             if (!(*iter).mGlobalValue.length()) {
@@ -444,7 +470,7 @@ MD5AnimParser::MD5AnimParser(SectionList &mSections) {
             // now read all elements (continuous list of floats)
             for (const auto &elem : (*iter).mElements) {
                 const char *sz = elem.szStart;
-                while (SkipSpacesAndLineEnd(&sz)) {
+                while (SkipSpacesAndLineEnd(&sz, elem.end)) {
                     float f;
                     sz = fast_atoreal_move<float>(sz, f);
                     desc.mValues.push_back(f);
@@ -471,11 +497,11 @@ MD5AnimParser::MD5AnimParser(SectionList &mSections) {
 
 // ------------------------------------------------------------------------------------------------
 // .MD5CAMERA parsing function
-MD5CameraParser::MD5CameraParser(SectionList &mSections) {
+MD5CameraParser::MD5CameraParser(SectionArray &mSections) {
     ASSIMP_LOG_DEBUG("MD5CameraParser begin");
     fFrameRate = 24.0f;
 
-    for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end(); iter != iterEnd; ++iter) {
+    for (SectionArray::const_iterator iter = mSections.begin(), iterEnd = mSections.end(); iter != iterEnd; ++iter) {
         if ((*iter).mName == "numFrames") {
             frames.reserve(strtoul10((*iter).mGlobalValue.c_str()));
         } else if ((*iter).mName == "frameRate") {
@@ -492,9 +518,9 @@ MD5CameraParser::MD5CameraParser(SectionList &mSections) {
 
                 frames.emplace_back();
                 CameraAnimFrameDesc &cur = frames.back();
-                AI_MD5_READ_TRIPLE(cur.vPositionXYZ);
-                AI_MD5_READ_TRIPLE(cur.vRotationQuat);
-                AI_MD5_SKIP_SPACES();
+                AI_MD5_READ_TRIPLE(cur.vPositionXYZ, &sz, elem.end, elem.iLineNumber);
+                AI_MD5_READ_TRIPLE(cur.vRotationQuat, &sz, elem.end, elem.iLineNumber);
+                AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
                 cur.fFOV = fast_atof(sz);
             }
         }

+ 31 - 29
code/AssetLib/MD5/MD5Parser.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2023, assimp team
+Copyright (c) 2006-2024, assimp team
 
 All rights reserved.
 
@@ -68,12 +68,14 @@ struct Element {
     //! Elements are terminated with \0
     char* szStart;
 
+    const char *end;
+
     //! Original line number (can be used in error messages
     //! if a parsing error occurs)
     unsigned int iLineNumber;
 };
 
-using ElementList = std::vector<Element>;
+using ElementArray = std::vector<Element>;
 
 // ---------------------------------------------------------------------------
 /** Represents a section of a MD5 file (such as the mesh or the joints section)
@@ -86,7 +88,7 @@ struct Section {
     unsigned int iLineNumber;
 
     //! List of all elements which have been parsed in this section.
-    ElementList mElements;
+    ElementArray mElements;
 
     //! Name of the section
     std::string mName;
@@ -96,7 +98,7 @@ struct Section {
     std::string mGlobalValue;
 };
 
-using SectionList = std::vector<Section>;
+using SectionArray = std::vector<Section>;
 
 // ---------------------------------------------------------------------------
 /** Basic information about a joint
@@ -132,7 +134,7 @@ struct BoneDesc : BaseJointDescription {
     unsigned int mMap;
 };
 
-using BoneList = std::vector<BoneDesc>;
+using BoneArray = std::vector<BoneDesc>;
 
 // ---------------------------------------------------------------------------
 /** Represents a bone (joint) descriptor in a MD5Anim file
@@ -145,7 +147,7 @@ struct AnimBoneDesc : BaseJointDescription {
     unsigned int iFirstKeyIndex;
 };
 
-using AnimBoneList = std::vector< AnimBoneDesc >;
+using AnimBoneArray = std::vector< AnimBoneDesc >;
 
 // ---------------------------------------------------------------------------
 /** Represents a base frame descriptor in a MD5Anim file
@@ -155,7 +157,7 @@ struct BaseFrameDesc {
     aiVector3D vRotationQuat;
 };
 
-using BaseFrameList = std::vector<BaseFrameDesc>;
+using BaseFrameArray = std::vector<BaseFrameDesc>;
 
 // ---------------------------------------------------------------------------
 /** Represents a camera animation frame in a MDCamera file
@@ -164,7 +166,7 @@ struct CameraAnimFrameDesc : BaseFrameDesc {
     float fFOV;
 };
 
-using CameraFrameList = std::vector<CameraAnimFrameDesc>;
+using CameraFrameArray = std::vector<CameraAnimFrameDesc>;
 
 // ---------------------------------------------------------------------------
 /** Represents a frame descriptor in a MD5Anim file
@@ -177,7 +179,7 @@ struct FrameDesc {
     std::vector< float > mValues;
 };
 
-using FrameList = std::vector<FrameDesc>;
+using FrameArray = std::vector<FrameDesc>;
 
 // ---------------------------------------------------------------------------
 /** Represents a vertex  descriptor in a MD5 file
@@ -199,7 +201,7 @@ struct VertexDesc {
     unsigned int mNumWeights;
 };
 
-using VertexList = std::vector<VertexDesc>;
+using VertexArray = std::vector<VertexDesc>;
 
 // ---------------------------------------------------------------------------
 /** Represents a vertex weight descriptor in a MD5 file
@@ -216,27 +218,27 @@ struct WeightDesc {
     aiVector3D vOffsetPosition;
 };
 
-using WeightList = std::vector<WeightDesc>;
-using FaceList   = std::vector<aiFace>;
+using WeightArray = std::vector<WeightDesc>;
+using FaceArray   = std::vector<aiFace>;
 
 // ---------------------------------------------------------------------------
 /** Represents a mesh in a MD5 file
 */
 struct MeshDesc {
     //! Weights of the mesh
-    WeightList mWeights;
+    WeightArray mWeights;
 
     //! Vertices of the mesh
-    VertexList mVertices;
+    VertexArray mVertices;
 
     //! Faces of the mesh
-    FaceList mFaces;
+    FaceArray mFaces;
 
     //! Name of the shader (=texture) to be assigned to the mesh
     aiString mShader;
 };
 
-using MeshList = std::vector<MeshDesc>;
+using MeshArray = std::vector<MeshDesc>;
 
 // ---------------------------------------------------------------------------
 // Convert a quaternion to its usual representation
@@ -269,13 +271,13 @@ public:
      *
      *  @param mSections List of file sections (output of MD5Parser)
      */
-    explicit MD5MeshParser(SectionList& mSections);
+    explicit MD5MeshParser(SectionArray& mSections);
 
     //! List of all meshes
-    MeshList mMeshes;
+    MeshArray mMeshes;
 
     //! List of all joints
-    BoneList mJoints;
+    BoneArray mJoints;
 };
 
 // remove this flag if you need to the bounding box data
@@ -292,20 +294,20 @@ public:
      *
      *  @param mSections List of file sections (output of MD5Parser)
      */
-    explicit MD5AnimParser(SectionList& mSections);
+    explicit MD5AnimParser(SectionArray& mSections);
 
 
     //! Output frame rate
     float fFrameRate;
 
     //! List of animation bones
-    AnimBoneList mAnimatedBones;
+    AnimBoneArray mAnimatedBones;
 
     //! List of base frames
-    BaseFrameList mBaseFrames;
+    BaseFrameArray mBaseFrames;
 
     //! List of animation frames
-    FrameList mFrames;
+    FrameArray mFrames;
 
     //! Number of animated components
     unsigned int mNumAnimatedComponents;
@@ -322,7 +324,7 @@ public:
      *
      *  @param mSections List of file sections (output of MD5Parser)
      */
-    explicit MD5CameraParser(SectionList& mSections);
+    explicit MD5CameraParser(SectionArray& mSections);
 
     //! Output frame rate
     float fFrameRate;
@@ -331,7 +333,7 @@ public:
     std::vector<unsigned int> cuts;
 
     //! Frames
-    CameraFrameList frames;
+    CameraFrameArray frames;
 };
 
 // ---------------------------------------------------------------------------
@@ -375,7 +377,7 @@ public:
     void ReportWarning (const char* warn);
 
     //! List of all sections which have been read
-    SectionList mSections;
+    SectionArray mSections;
 
 private:
     bool ParseSection(Section& out);
@@ -388,7 +390,7 @@ private:
 
 private:
     char* buffer;
-    char* bufferEnd;
+    const char* bufferEnd;
     unsigned int fileSize;
     unsigned int lineNumber;
 };
@@ -406,7 +408,7 @@ inline void MD5Parser::ReportError(const char* error) {
 // -------------------------------------------------------------------
 inline bool MD5Parser::SkipLine(const char* in, const char** out) {
     ++lineNumber;
-    return Assimp::SkipLine(in ,out);
+    return Assimp::SkipLine(in, out, bufferEnd);
 }
 
 // -------------------------------------------------------------------
@@ -450,7 +452,7 @@ inline bool MD5Parser::SkipSpacesAndLineEnd() {
 
 // -------------------------------------------------------------------
 inline bool MD5Parser::SkipSpaces() {
-    return Assimp::SkipSpaces((const char**)&buffer);
+    return Assimp::SkipSpaces((const char**)&buffer, bufferEnd);
 }
 
 } // namespace Assimp

+ 31 - 36
code/AssetLib/NFF/NFFLoader.cpp

@@ -85,7 +85,7 @@ const aiImporterDesc *NFFImporter::GetInfo() const {
 
 // ------------------------------------------------------------------------------------------------
 #define AI_NFF_PARSE_FLOAT(f) \
-    SkipSpaces(&sz);          \
+    SkipSpaces(&sz, lineEnd);          \
     if (!IsLineEnd(*sz)) sz = fast_atoreal_move<ai_real>(sz, (ai_real &)f);
 
 // ------------------------------------------------------------------------------------------------
@@ -111,7 +111,7 @@ const aiImporterDesc *NFFImporter::GetInfo() const {
             ASSIMP_LOG_WARN("NFF2: Unexpected EOF, can't read next token"); \
             break;                                                          \
         }                                                                   \
-        SkipSpaces(line, &sz);                                              \
+        SkipSpaces(line, &sz, lineEnd);                                              \
     } while (IsLineEnd(*sz))
 
 // ------------------------------------------------------------------------------------------------
@@ -148,9 +148,9 @@ void NFFImporter::LoadNFF2MaterialTable(std::vector<ShadingInfo> &output,
 
     // No read the file line per line
     char line[4096];
-    const char *sz;
+    const char *sz, *lineEnd = &line[2095]+1;
     while (GetNextLine(buffer, line)) {
-        SkipSpaces(line, &sz);
+        SkipSpaces(line, &sz, lineEnd);
 
         // 'version' defines the version of the file format
         if (TokenMatch(sz, "version", 7)) {
@@ -198,18 +198,16 @@ void NFFImporter::LoadNFF2MaterialTable(std::vector<ShadingInfo> &output,
 
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
-void NFFImporter::InternReadFile(const std::string &pFile,
-        aiScene *pScene, IOSystem *pIOHandler) {
-    std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
-
-    // Check whether we can read from the file
-    if (!file)
-        throw DeadlyImportError("Failed to open NFF file ", pFile, ".");
+void NFFImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSystem *pIOHandler) {
+    std::unique_ptr<IOStream> stream(pIOHandler->Open(file, "rb"));
+    if (!stream) {
+        throw DeadlyImportError("Failed to open NFF file ", file, ".");
+    }
 
     // allocate storage and copy the contents of the file to a memory buffer
     // (terminate it with zero)
     std::vector<char> mBuffer2;
-    TextFileToBuffer(file.get(), mBuffer2);
+    TextFileToBuffer(stream.get(), mBuffer2);
     const char *buffer = &mBuffer2[0];
 
     // mesh arrays - separate here to make the handling of the pointers below easier.
@@ -219,8 +217,10 @@ void NFFImporter::InternReadFile(const std::string &pFile,
     std::vector<MeshInfo> meshesLocked;
 
     char line[4096];
+    const char *lineEnd = &line[4096];
     const char *sz;
 
+
     // camera parameters
     aiVector3D camPos, camUp(0.f, 1.f, 0.f), camLookAt(0.f, 0.f, 1.f);
     ai_real angle = 45.f;
@@ -265,7 +265,7 @@ void NFFImporter::InternReadFile(const std::string &pFile,
         CommentRemover::RemoveLineComments("//", &mBuffer2[0]);
 
         while (GetNextLine(buffer, line)) {
-            SkipSpaces(line, &sz);
+            SkipSpaces(line, &sz, lineEnd);
             if (TokenMatch(sz, "version", 7)) {
                 ASSIMP_LOG_INFO("NFF (Sense8) file format: ", sz);
             } else if (TokenMatch(sz, "viewpos", 7)) {
@@ -295,7 +295,7 @@ void NFFImporter::InternReadFile(const std::string &pFile,
 
                     // material table - an external file
                     if (TokenMatch(sz, "mtable", 6)) {
-                        SkipSpaces(&sz);
+                        SkipSpaces(&sz, lineEnd);
                         sz3 = sz;
                         while (!IsSpaceOrNewLine(*sz))
                             ++sz;
@@ -316,12 +316,12 @@ void NFFImporter::InternReadFile(const std::string &pFile,
                             std::string::size_type sepPos;
                             if ((std::string::npos == (sepPos = path.find_last_of('\\')) || !sepPos) &&
                                     (std::string::npos == (sepPos = path.find_last_of('/')) || !sepPos)) {
-                                sepPos = pFile.find_last_of('\\');
+                                sepPos = file.find_last_of('\\');
                                 if (std::string::npos == sepPos) {
-                                    sepPos = pFile.find_last_of('/');
+                                    sepPos = file.find_last_of('/');
                                 }
                                 if (std::string::npos != sepPos) {
-                                    path = pFile.substr(0, sepPos + 1) + path;
+                                    path = file.substr(0, sepPos + 1) + path;
                                 }
                             }
                             LoadNFF2MaterialTable(materialTable, path, pIOHandler);
@@ -351,7 +351,7 @@ void NFFImporter::InternReadFile(const std::string &pFile,
 
                     // parse all other attributes in the line
                     while (true) {
-                        SkipSpaces(&sz);
+                        SkipSpaces(&sz, lineEnd);
                         if (IsLineEnd(*sz)) break;
 
                         // color definition
@@ -403,23 +403,20 @@ void NFFImporter::InternReadFile(const std::string &pFile,
                 tempIdx.reserve(10);
                 for (unsigned int i = 0; i < num; ++i) {
                     AI_NFF2_GET_NEXT_TOKEN();
-                    SkipSpaces(line, &sz);
+                    SkipSpaces(line, &sz, lineEnd);
                     unsigned int numIdx = strtoul10(sz, &sz);
 
                     // read all faces indices
                     if (numIdx) {
-                        // mesh.faces.push_back(numIdx);
-                        // tempIdx.erase(tempIdx.begin(),tempIdx.end());
                         tempIdx.resize(numIdx);
 
                         for (unsigned int a = 0; a < numIdx; ++a) {
-                            SkipSpaces(sz, &sz);
+                            SkipSpaces(sz, &sz, lineEnd);
                             unsigned int m = strtoul10(sz, &sz);
                             if (m >= (unsigned int)tempPositions.size()) {
                                 ASSIMP_LOG_ERROR("NFF2: Vertex index overflow");
                                 m = 0;
                             }
-                            // mesh.vertices.push_back (tempPositions[idx]);
                             tempIdx[a] = m;
                         }
                     }
@@ -432,7 +429,7 @@ void NFFImporter::InternReadFile(const std::string &pFile,
                     shader.color = aiColor3D(1.f, 1.f, 1.f);
                     aiColor4D c = aiColor4D(1.f, 1.f, 1.f, 1.f);
                     while (true) {
-                        SkipSpaces(sz, &sz);
+                        SkipSpaces(sz, &sz, lineEnd);
                         if (IsLineEnd(*sz)) break;
 
                         // per-polygon colors
@@ -510,7 +507,7 @@ void NFFImporter::InternReadFile(const std::string &pFile,
 
                         // Material ID?
                         else if (!materialTable.empty() && TokenMatch(sz, "matid", 5)) {
-                            SkipSpaces(&sz);
+                            SkipSpaces(&sz, lineEnd);
                             matIdx = strtoul10(sz, &sz);
                             if (matIdx >= materialTable.size()) {
                                 ASSIMP_LOG_ERROR("NFF2: Material index overflow.");
@@ -527,7 +524,7 @@ void NFFImporter::InternReadFile(const std::string &pFile,
                             shader.specular = mat.specular;
                             shader.shininess = mat.shininess;
                         } else
-                            SkipToken(sz);
+                            SkipToken(sz, lineEnd);
                     }
 
                     // search the list of all shaders we have for this object whether
@@ -649,7 +646,7 @@ void NFFImporter::InternReadFile(const std::string &pFile,
                     sz = &line[1];
                     out = currentMesh;
                 }
-                SkipSpaces(sz, &sz);
+                SkipSpaces(sz, &sz, lineEnd);
                 unsigned int m = strtoul10(sz);
 
                 // ---- flip the face order
@@ -677,13 +674,13 @@ void NFFImporter::InternReadFile(const std::string &pFile,
                     }
                     if (out == currentMeshWithUVCoords) {
                         // FIX: in one test file this wraps over multiple lines
-                        SkipSpaces(&sz);
+                        SkipSpaces(&sz, lineEnd);
                         if (IsLineEnd(*sz)) {
                             GetNextLine(buffer, line);
                             sz = line;
                         }
                         AI_NFF_PARSE_FLOAT(v.x);
-                        SkipSpaces(&sz);
+                        SkipSpaces(&sz, lineEnd);
                         if (IsLineEnd(*sz)) {
                             GetNextLine(buffer, line);
                             sz = line;
@@ -717,7 +714,7 @@ void NFFImporter::InternReadFile(const std::string &pFile,
                 // if the next one is NOT a number we assume it is a texture file name
                 // this feature is used by some NFF files on the internet and it has
                 // been implemented as it can be really useful
-                SkipSpaces(&sz);
+                SkipSpaces(&sz, lineEnd);
                 if (!IsNumeric(*sz)) {
                     // TODO: Support full file names with spaces and quotation marks ...
                     const char *p = sz;
@@ -731,10 +728,8 @@ void NFFImporter::InternReadFile(const std::string &pFile,
                 } else {
                     AI_NFF_PARSE_FLOAT(s.ambient); // optional
                 }
-            }
-            // 'shader' - other way to specify a texture
-            else if (TokenMatch(sz, "shader", 6)) {
-                SkipSpaces(&sz);
+            } else if (TokenMatch(sz, "shader", 6)) { // 'shader' - other way to specify a texture
+                SkipSpaces(&sz, lineEnd);
                 const char *old = sz;
                 while (!IsSpaceOrNewLine(*sz))
                     ++sz;
@@ -889,7 +884,7 @@ void NFFImporter::InternReadFile(const std::string &pFile,
             }
             // 'tess' - tessellation
             else if (TokenMatch(sz, "tess", 4)) {
-                SkipSpaces(&sz);
+                SkipSpaces(&sz, lineEnd);
                 iTesselation = strtoul10(sz);
             }
             // 'from' - camera position
@@ -929,7 +924,7 @@ void NFFImporter::InternReadFile(const std::string &pFile,
             // '' - comment
             else if ('#' == line[0]) {
                 const char *space;
-                SkipSpaces(&line[1], &space);
+                SkipSpaces(&line[1], &space, lineEnd);
                 if (!IsLineEnd(*space)) {
                     ASSIMP_LOG_INFO(space);
                 }

+ 17 - 16
code/AssetLib/OFF/OFFLoader.cpp

@@ -84,10 +84,10 @@ const aiImporterDesc *OFFImporter::GetInfo() const {
 
 // skip blank space, lines and comments
 static void NextToken(const char **car, const char *end) {
-    SkipSpacesAndLineEnd(car);
+    SkipSpacesAndLineEnd(car, end);
     while (*car < end && (**car == '#' || **car == '\n' || **car == '\r')) {
-        SkipLine(car);
-        SkipSpacesAndLineEnd(car);
+        SkipLine(car, end);
+        SkipSpacesAndLineEnd(car, end);
     }
 }
 
@@ -195,6 +195,7 @@ void OFFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
     char line[4096];
     buffer = car;
     const char *sz = car;
+    const char *lineEnd = &line[4096];
 
     // now read all vertex lines
     for (unsigned int i = 0; i < numVertices; ++i) {
@@ -210,13 +211,13 @@ void OFFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 
         // stop at dimensions: this allows loading 1D or 2D coordinate vertices
         for (unsigned int dim = 0; dim < dimensions; ++dim) {
-            SkipSpaces(&sz);
+            SkipSpaces(&sz, lineEnd);
             sz = fast_atoreal_move<ai_real>(sz, *vec[dim]);
         }
 
         // if has homogeneous coordinate, divide others by this one
         if (hasHomogenous) {
-            SkipSpaces(&sz);
+            SkipSpaces(&sz, lineEnd);
             ai_real w = 1.;
             sz = fast_atoreal_move<ai_real>(sz, w);
             for (unsigned int dim = 0; dim < dimensions; ++dim) {
@@ -227,11 +228,11 @@ void OFFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
         // read optional normals
         if (hasNormals) {
             aiVector3D &n = mesh->mNormals[i];
-            SkipSpaces(&sz);
+            SkipSpaces(&sz, lineEnd);
             sz = fast_atoreal_move<ai_real>(sz, (ai_real &)n.x);
-            SkipSpaces(&sz);
+            SkipSpaces(&sz, lineEnd);
             sz = fast_atoreal_move<ai_real>(sz, (ai_real &)n.y);
-            SkipSpaces(&sz);
+            SkipSpaces(&sz, lineEnd);
             fast_atoreal_move<ai_real>(sz, (ai_real &)n.z);
         }
 
@@ -241,22 +242,22 @@ void OFFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
         // in theory should be testing type !
         if (hasColors) {
             aiColor4D &c = mesh->mColors[0][i];
-            SkipSpaces(&sz);
+            SkipSpaces(&sz, lineEnd);
             sz = fast_atoreal_move<ai_real>(sz, (ai_real &)c.r);
             if (*sz != '#' && *sz != '\n' && *sz != '\r') {
-                SkipSpaces(&sz);
+                SkipSpaces(&sz, lineEnd);
                 sz = fast_atoreal_move<ai_real>(sz, (ai_real &)c.g);
             } else {
                 c.g = 0.;
             }
             if (*sz != '#' && *sz != '\n' && *sz != '\r') {
-                SkipSpaces(&sz);
+                SkipSpaces(&sz, lineEnd);
                 sz = fast_atoreal_move<ai_real>(sz, (ai_real &)c.b);
             } else {
                 c.b = 0.;
             }
             if (*sz != '#' && *sz != '\n' && *sz != '\r') {
-                SkipSpaces(&sz);
+                SkipSpaces(&sz, lineEnd);
                 sz = fast_atoreal_move<ai_real>(sz, (ai_real &)c.a);
             } else {
                 c.a = 1.;
@@ -264,9 +265,9 @@ void OFFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
         }
         if (hasTexCoord) {
             aiVector3D &t = mesh->mTextureCoords[0][i];
-            SkipSpaces(&sz);
+            SkipSpaces(&sz, lineEnd);
             sz = fast_atoreal_move<ai_real>(sz, (ai_real &)t.x);
-            SkipSpaces(&sz);
+            SkipSpaces(&sz, lineEnd);
             fast_atoreal_move<ai_real>(sz, (ai_real &)t.y);
         }
     }
@@ -280,7 +281,7 @@ void OFFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
         }
         unsigned int idx;
         sz = line;
-        SkipSpaces(&sz);
+        SkipSpaces(&sz, lineEnd);
         idx = strtoul10(sz, &sz);
         if (!idx || idx > 9) {
             ASSIMP_LOG_ERROR("OFF: Faces with zero indices aren't allowed");
@@ -291,7 +292,7 @@ void OFFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
         faces->mNumIndices = idx;
         faces->mIndices = new unsigned int[faces->mNumIndices];
         for (unsigned int m = 0; m < faces->mNumIndices; ++m) {
-            SkipSpaces(&sz);
+            SkipSpaces(&sz, lineEnd);
             idx = strtoul10(sz, &sz);
             if (idx >= numVertices) {
                 ASSIMP_LOG_ERROR("OFF: Vertex index is out of range");

+ 11 - 8
code/AssetLib/Obj/ObjFileParser.cpp

@@ -64,6 +64,7 @@ ObjFileParser::ObjFileParser() :
         m_pModel(nullptr),
         m_uiLine(0),
         m_buffer(),
+        mEnd(&m_buffer[Buffersize]),
         m_pIO(nullptr),
         m_progress(nullptr),
         m_originalObjFileName() {
@@ -97,8 +98,6 @@ ObjFileParser::ObjFileParser(IOStreamBuffer<char> &streamBuffer, const std::stri
     parseFile(streamBuffer);
 }
 
-ObjFileParser::~ObjFileParser() = default;
-
 void ObjFileParser::setBuffer(std::vector<char> &buffer) {
     m_DataIt = buffer.begin();
     m_DataItEnd = buffer.end();
@@ -121,6 +120,7 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
     while (streamBuffer.getNextDataLine(buffer, '\\')) {
         m_DataIt = buffer.begin();
         m_DataItEnd = buffer.end();
+        mEnd = &buffer[buffer.size() - 1] + 1;
 
         // Handle progress reporting
         const size_t filePos(streamBuffer.getFilePos());
@@ -130,7 +130,7 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
             m_progress->UpdateFileRead(processed, progressTotal);
         }
 
-        // handle cstype section end (http://paulbourke.net/dataformats/obj/)
+        // handle c-stype section end (http://paulbourke.net/dataformats/obj/)
         if (insideCstype) {
             switch (*m_DataIt) {
             case 'e': {
@@ -301,18 +301,19 @@ size_t ObjFileParser::getNumComponentsInDataDefinition() {
         } else if (IsLineEnd(*tmp)) {
             end_of_definition = true;
         }
-        if (!SkipSpaces(&tmp)) {
+        if (!SkipSpaces(&tmp, mEnd)) {
             break;
         }
         const bool isNum(IsNumeric(*tmp) || isNanOrInf(tmp));
-        SkipToken(tmp);
+        SkipToken(tmp, mEnd);
         if (isNum) {
             ++numComponents;
         }
-        if (!SkipSpaces(&tmp)) {
+        if (!SkipSpaces(&tmp, mEnd)) {
             break;
         }
     }
+    
     return numComponents;
 }
 
@@ -487,8 +488,9 @@ void ObjFileParser::getFace(aiPrimitiveType type) {
                 ++iStep;
             }
 
-            if (iPos == 1 && !vt && vn)
+            if (iPos == 1 && !vt && vn) {
                 iPos = 2; // skip texture coords for normals if there are no tex coords
+            }
 
             if (iVal > 0) {
                 // Store parsed index
@@ -577,8 +579,9 @@ void ObjFileParser::getMaterialDesc() {
     // Get name
     std::string strName(pStart, &(*m_DataIt));
     strName = trim_whitespaces(strName);
-    if (strName.empty())
+    if (strName.empty()) {
         skip = true;
+    }
 
     // If the current mesh has the same material, we simply ignore that 'usemtl' command
     // There is no need to create another object or even mesh here

+ 4 - 9
code/AssetLib/Obj/ObjFileParser.h

@@ -41,6 +41,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef OBJ_FILEPARSER_H_INC
 #define OBJ_FILEPARSER_H_INC
 
+#include "ObjFileData.h"
+
 #include <assimp/IOStreamBuffer.h>
 #include <assimp/material.h>
 #include <assimp/mesh.h>
@@ -53,14 +55,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace Assimp {
 
-namespace ObjFile {
-struct Model;
-struct Object;
-struct Material;
-struct Point3;
-struct Point2;
-} // namespace ObjFile
-
 class ObjFileImporter;
 class IOSystem;
 class ProgressHandler;
@@ -79,7 +73,7 @@ public:
     /// @brief  Constructor with data array.
     ObjFileParser(IOStreamBuffer<char> &streamBuffer, const std::string &modelName, IOSystem *io, ProgressHandler *progress, const std::string &originalObjFileName);
     /// @brief  Destructor
-    ~ObjFileParser();
+    ~ObjFileParser() = default;
     /// @brief  If you want to load in-core data.
     void setBuffer(std::vector<char> &buffer);
     /// @brief  Model getter.
@@ -149,6 +143,7 @@ private:
     unsigned int m_uiLine;
     //! Helper buffer
     char m_buffer[Buffersize];
+    const char *mEnd; 
     /// Pointer to IO system instance.
     IOSystem *m_pIO;
     //! Pointer to progress handler

+ 3 - 2
code/AssetLib/Ply/PlyLoader.cpp

@@ -159,7 +159,8 @@ void PLYImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
     mBuffer = (unsigned char *)&mBuffer2[0];
 
     char *szMe = (char *)&this->mBuffer[0];
-    SkipSpacesAndLineEnd(szMe, (const char **)&szMe);
+    const char *end = &mBuffer2[0] + mBuffer2.size();
+    SkipSpacesAndLineEnd(szMe, (const char **)&szMe, end);
 
     // determine the format of the file data and construct the aiMesh
     PLY::DOM sPlyDom;
@@ -167,7 +168,7 @@ void PLYImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 
     if (TokenMatch(szMe, "format", 6)) {
         if (TokenMatch(szMe, "ascii", 5)) {
-            SkipLine(szMe, (const char **)&szMe);
+            SkipLine(szMe, (const char **)&szMe, end);
             if (!PLY::DOM::ParseInstance(streamedBuffer, &sPlyDom, this)) {
                 if (mGeneratedMesh != nullptr) {
                     delete (mGeneratedMesh);

+ 33 - 28
code/AssetLib/Ply/PlyParser.cpp

@@ -50,7 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/DefaultLogger.hpp>
 #include <utility>
 
-using namespace Assimp;
+namespace Assimp {
 
 // ------------------------------------------------------------------------------------------------
 PLY::EDataType PLY::Property::ParseDataType(std::vector<char> &buffer) {
@@ -296,7 +296,7 @@ bool PLY::Element::ParseElement(IOStreamBuffer<char> &streamBuffer, std::vector<
         return true;
     }
 
-    //parse the number of occurrences of this element
+    // parse the number of occurrences of this element
     const char *pCur = (char *)&buffer[0];
     pOut->NumOccur = strtoul10(pCur, &pCur);
 
@@ -321,13 +321,13 @@ bool PLY::Element::ParseElement(IOStreamBuffer<char> &streamBuffer, std::vector<
     return true;
 }
 
-// ------------------------------------------------------------------------------------------------
 bool PLY::DOM::SkipSpaces(std::vector<char> &buffer) {
     const char *pCur = buffer.empty() ? nullptr : (char *)&buffer[0];
+    const char *end = pCur + buffer.size();
     bool ret = false;
     if (pCur) {
         const char *szCur = pCur;
-        ret = Assimp::SkipSpaces(pCur, &pCur);
+        ret = Assimp::SkipSpaces(pCur, &pCur, end);
 
         uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur;
         buffer.erase(buffer.begin(), buffer.begin() + iDiff);
@@ -339,10 +339,11 @@ bool PLY::DOM::SkipSpaces(std::vector<char> &buffer) {
 
 bool PLY::DOM::SkipLine(std::vector<char> &buffer) {
     const char *pCur = buffer.empty() ? nullptr : (char *)&buffer[0];
+    const char *end = pCur + buffer.size();
     bool ret = false;
     if (pCur) {
         const char *szCur = pCur;
-        ret = Assimp::SkipLine(pCur, &pCur);
+        ret = Assimp::SkipLine(pCur, &pCur, end);
 
         uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur;
         buffer.erase(buffer.begin(), buffer.begin() + iDiff);
@@ -369,10 +370,11 @@ bool PLY::DOM::TokenMatch(std::vector<char> &buffer, const char *token, unsigned
 
 bool PLY::DOM::SkipSpacesAndLineEnd(std::vector<char> &buffer) {
     const char *pCur = buffer.empty() ? nullptr : (char *)&buffer[0];
+    const char *end = pCur + buffer.size();
     bool ret = false;
     if (pCur) {
         const char *szCur = pCur;
-        ret = Assimp::SkipSpacesAndLineEnd(pCur, &pCur);
+        ret = Assimp::SkipSpacesAndLineEnd(pCur, &pCur, end);
 
         uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur;
         buffer.erase(buffer.begin(), buffer.begin() + iDiff);
@@ -426,7 +428,7 @@ bool PLY::DOM::ParseHeader(IOStreamBuffer<char> &streamBuffer, std::vector<char>
         } else {
             // ignore unknown header elements
             if (!streamBuffer.getNextLine(buffer))
-              return false;
+                return false;
         }
     }
 
@@ -446,7 +448,7 @@ bool PLY::DOM::ParseElementInstanceLists(IOStreamBuffer<char> &streamBuffer, std
     std::vector<PLY::ElementInstanceList>::iterator a = alElementData.begin();
 
     // parse all element instances
-    //construct vertices and faces
+    // construct vertices and faces
     for (; i != alElements.end(); ++i, ++a) {
         if ((*i).eSemantic == EEST_Vertex || (*i).eSemantic == EEST_Face || (*i).eSemantic == EEST_TriStrip) {
             PLY::ElementInstanceList::ParseInstanceList(streamBuffer, buffer, &(*i), nullptr, loader);
@@ -528,7 +530,7 @@ bool PLY::DOM::ParseInstance(IOStreamBuffer<char> &streamBuffer, DOM *p_pcOut, P
         return false;
     }
 
-    //get next line after header
+    // get next line after header
     streamBuffer.getNextLine(buffer);
     if (!p_pcOut->ParseElementInstanceLists(streamBuffer, buffer, loader)) {
         ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstance() failure");
@@ -558,23 +560,24 @@ bool PLY::ElementInstanceList::ParseInstanceList(
         }
     } else {
         const char *pCur = (const char *)&buffer[0];
+        const char *end = pCur + buffer.size();
         // be sure to have enough storage
         for (unsigned int i = 0; i < pcElement->NumOccur; ++i) {
             if (p_pcOut)
-                PLY::ElementInstance::ParseInstance(pCur, pcElement, &p_pcOut->alInstances[i]);
+                PLY::ElementInstance::ParseInstance(pCur, end, pcElement, &p_pcOut->alInstances[i]);
             else {
                 ElementInstance elt;
-                PLY::ElementInstance::ParseInstance(pCur, pcElement, &elt);
+                PLY::ElementInstance::ParseInstance(pCur, end, pcElement, &elt);
 
                 // Create vertex or face
                 if (pcElement->eSemantic == EEST_Vertex) {
-                    //call loader instance from here
+                    // call loader instance from here
                     loader->LoadVertex(pcElement, &elt, i);
                 } else if (pcElement->eSemantic == EEST_Face) {
-                    //call loader instance from here
+                    // call loader instance from here
                     loader->LoadFace(pcElement, &elt, i);
                 } else if (pcElement->eSemantic == EEST_TriStrip) {
-                    //call loader instance from here
+                    // call loader instance from here
                     loader->LoadFace(pcElement, &elt, i);
                 }
             }
@@ -611,13 +614,13 @@ bool PLY::ElementInstanceList::ParseInstanceListBinary(
 
             // Create vertex or face
             if (pcElement->eSemantic == EEST_Vertex) {
-                //call loader instance from here
+                // call loader instance from here
                 loader->LoadVertex(pcElement, &elt, i);
             } else if (pcElement->eSemantic == EEST_Face) {
-                //call loader instance from here
+                // call loader instance from here
                 loader->LoadFace(pcElement, &elt, i);
             } else if (pcElement->eSemantic == EEST_TriStrip) {
-                //call loader instance from here
+                // call loader instance from here
                 loader->LoadFace(pcElement, &elt, i);
             }
         }
@@ -626,7 +629,7 @@ bool PLY::ElementInstanceList::ParseInstanceListBinary(
 }
 
 // ------------------------------------------------------------------------------------------------
-bool PLY::ElementInstance::ParseInstance(const char *&pCur,
+bool PLY::ElementInstance::ParseInstance(const char *&pCur, const char *end,
         const PLY::Element *pcElement,
         PLY::ElementInstance *p_pcOut) {
     ai_assert(nullptr != pcElement);
@@ -638,7 +641,7 @@ bool PLY::ElementInstance::ParseInstance(const char *&pCur,
     std::vector<PLY::PropertyInstance>::iterator i = p_pcOut->alProperties.begin();
     std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin();
     for (; i != p_pcOut->alProperties.end(); ++i, ++a) {
-        if (!(PLY::PropertyInstance::ParseInstance(pCur, &(*a), &(*i)))) {
+        if (!(PLY::PropertyInstance::ParseInstance(pCur, end, &(*a), &(*i)))) {
             ASSIMP_LOG_WARN("Unable to parse property instance. "
                             "Skipping this element instance");
 
@@ -678,13 +681,13 @@ bool PLY::ElementInstance::ParseInstanceBinary(
 }
 
 // ------------------------------------------------------------------------------------------------
-bool PLY::PropertyInstance::ParseInstance(const char *&pCur,
-        const PLY::Property *prop, PLY::PropertyInstance *p_pcOut) {
+bool PLY::PropertyInstance::ParseInstance(const char *&pCur, const char *end, const PLY::Property *prop,
+        PLY::PropertyInstance *p_pcOut) {
     ai_assert(nullptr != prop);
     ai_assert(nullptr != p_pcOut);
 
     // skip spaces at the beginning
-    if (!SkipSpaces(&pCur)) {
+    if (!SkipSpaces(&pCur, end)) {
         return false;
     }
 
@@ -699,7 +702,7 @@ bool PLY::PropertyInstance::ParseInstance(const char *&pCur,
         // parse all list elements
         p_pcOut->avList.resize(iNum);
         for (unsigned int i = 0; i < iNum; ++i) {
-            if (!SkipSpaces(&pCur))
+            if (!SkipSpaces(&pCur, end))
                 return false;
 
             PLY::PropertyInstance::ParseValue(pCur, prop->eType, &p_pcOut->avList[i]);
@@ -711,7 +714,7 @@ bool PLY::PropertyInstance::ParseInstance(const char *&pCur,
         PLY::PropertyInstance::ParseValue(pCur, prop->eType, &v);
         p_pcOut->avList.push_back(v);
     }
-    SkipSpacesAndLineEnd(&pCur);
+    SkipSpacesAndLineEnd(&pCur, end);
     return true;
 }
 
@@ -774,7 +777,7 @@ bool PLY::PropertyInstance::ParseValue(const char *&pCur,
     ai_assert(nullptr != pCur);
     ai_assert(nullptr != out);
 
-    //calc element size
+    // calc element size
     bool ret = true;
     switch (eType) {
     case EDT_UInt:
@@ -824,7 +827,7 @@ bool PLY::PropertyInstance::ParseValueBinary(IOStreamBuffer<char> &streamBuffer,
         bool p_bBE) {
     ai_assert(nullptr != out);
 
-    //calc element size
+    // calc element size
     unsigned int lsize = 0;
     switch (eType) {
     case EDT_Char:
@@ -852,11 +855,11 @@ bool PLY::PropertyInstance::ParseValueBinary(IOStreamBuffer<char> &streamBuffer,
         break;
     }
 
-    //read the next file block if needed
+    // read the next file block if needed
     if (bufferSize < lsize) {
         std::vector<char> nbuffer;
         if (streamBuffer.getNextBlock(nbuffer)) {
-            //concat buffer contents
+            // concat buffer contents
             buffer = std::vector<char>(buffer.end() - bufferSize, buffer.end());
             buffer.insert(buffer.end(), nbuffer.begin(), nbuffer.end());
             nbuffer.clear();
@@ -958,4 +961,6 @@ bool PLY::PropertyInstance::ParseValueBinary(IOStreamBuffer<char> &streamBuffer,
     return ret;
 }
 
+} // namespace Assimp
+
 #endif // !! ASSIMP_BUILD_NO_PLY_IMPORTER

+ 2 - 2
code/AssetLib/Ply/PlyParser.h

@@ -324,7 +324,7 @@ public:
 
     // -------------------------------------------------------------------
     //! Parse a property instance
-    static bool ParseInstance(const char* &pCur,
+    static bool ParseInstance(const char* &pCur, const char *end,
         const Property* prop, PropertyInstance* p_pcOut);
 
     // -------------------------------------------------------------------
@@ -364,7 +364,7 @@ public:
 
     // -------------------------------------------------------------------
     //! Parse an element instance
-    static bool ParseInstance(const char* &pCur,
+    static bool ParseInstance(const char *&pCur, const char *end,
         const Element* pcElement, ElementInstance* p_pcOut);
 
     // -------------------------------------------------------------------

+ 5 - 4
code/AssetLib/Raw/RawLoader.cpp

@@ -104,11 +104,12 @@ void RAWImporter::InternReadFile(const std::string &pFile,
 
     // now read all lines
     char line[4096];
+    const char *end = &line[4096];
     while (GetNextLine(buffer, line)) {
         // if the line starts with a non-numeric identifier, it marks
         // the beginning of a new group
         const char *sz = line;
-        SkipSpaces(&sz);
+        SkipSpaces(&sz, end);
         if (IsLineEnd(*sz)) continue;
         if (!IsNumeric(*sz)) {
             const char *sz2 = sz;
@@ -117,8 +118,8 @@ void RAWImporter::InternReadFile(const std::string &pFile,
             const unsigned int length = (unsigned int)(sz2 - sz);
 
             // find an existing group with this name
-            for (std::vector<GroupInformation>::iterator it = outGroups.begin(), end = outGroups.end();
-                    it != end; ++it) {
+            for (std::vector<GroupInformation>::iterator it = outGroups.begin(), endIt = outGroups.end();
+                    it != endIt; ++it) {
                 if (length == (*it).name.length() && !::strcmp(sz, (*it).name.c_str())) {
                     curGroup = it;
                     sz2 = nullptr;
@@ -134,7 +135,7 @@ void RAWImporter::InternReadFile(const std::string &pFile,
             float data[12];
             unsigned int num;
             for (num = 0; num < 12; ++num) {
-                if (!SkipSpaces(&sz) || !IsNumeric(*sz)) break;
+                if (!SkipSpaces(&sz, end) || !IsNumeric(*sz)) break;
                 sz = fast_atoreal_move<float>(sz, data[num]);
             }
             if (num != 12 && num != 9) {

+ 72 - 72
code/AssetLib/SMD/SMDLoader.cpp

@@ -82,8 +82,10 @@ static constexpr aiImporterDesc desc = {
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 SMDImporter::SMDImporter() :
-        configFrameID(), 
-        pScene( nullptr ), 
+        configFrameID(),
+        mBuffer(),
+        mEnd(nullptr),
+        pScene(nullptr), 
         iFileSize( 0 ), 
         iSmallestFrame( INT_MAX ),
         dLengthOfAnim( 0.0 ),
@@ -92,9 +94,6 @@ SMDImporter::SMDImporter() :
     // empty
 }
 
-// ------------------------------------------------------------------------------------------------
-// Destructor, private as well
-SMDImporter::~SMDImporter() = default;
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
@@ -632,13 +631,13 @@ void SMDImporter::ParseFile() {
 
     // read line per line ...
     for ( ;; ) {
-        if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) {
+        if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent, mEnd)) {
             break;
         }
 
         // "version <n> \n", <n> should be 1 for hl and hl2 SMD files
         if (TokenMatch(szCurrent,"version",7)) {
-            if(!SkipSpaces(szCurrent,&szCurrent)) break;
+            if(!SkipSpaces(szCurrent,&szCurrent, mEnd)) break;
             if (1 != strtoul10(szCurrent,&szCurrent)) {
                 ASSIMP_LOG_WARN("SMD.version is not 1. This "
                     "file format is not known. Continuing happily ...");
@@ -647,26 +646,26 @@ void SMDImporter::ParseFile() {
         }
         // "nodes\n" - Starts the node section
         if (TokenMatch(szCurrent,"nodes",5)) {
-            ParseNodesSection(szCurrent,&szCurrent);
+            ParseNodesSection(szCurrent, &szCurrent, mEnd);
             continue;
         }
         // "triangles\n" - Starts the triangle section
         if (TokenMatch(szCurrent,"triangles",9)) {
-            ParseTrianglesSection(szCurrent,&szCurrent);
+            ParseTrianglesSection(szCurrent, &szCurrent, mEnd);
             continue;
         }
         // "vertexanimation\n" - Starts the vertex animation section
         if (TokenMatch(szCurrent,"vertexanimation",15)) {
             bHasUVs = false;
-            ParseVASection(szCurrent,&szCurrent);
+            ParseVASection(szCurrent, &szCurrent, mEnd);
             continue;
         }
         // "skeleton\n" - Starts the skeleton section
         if (TokenMatch(szCurrent,"skeleton",8)) {
-            ParseSkeletonSection(szCurrent,&szCurrent);
+            ParseSkeletonSection(szCurrent, &szCurrent, mEnd);
             continue;
         }
-        SkipLine(szCurrent,&szCurrent);
+        SkipLine(szCurrent, &szCurrent, mEnd);
     }
 }
 
@@ -683,6 +682,7 @@ void SMDImporter::ReadSmd(const std::string &pFile, IOSystem* pIOHandler) {
     // Allocate storage and copy the contents of the file to a memory buffer
     mBuffer.resize(iFileSize + 1);
     TextFileToBuffer(file.get(), mBuffer);
+    mEnd = &mBuffer[mBuffer.size() - 1] + 1;
 
     iSmallestFrame = INT_MAX;
     bHasUVs = true;
@@ -723,26 +723,26 @@ unsigned int SMDImporter::GetTextureIndex(const std::string& filename) {
 
 // ------------------------------------------------------------------------------------------------
 // Parse the nodes section of the file
-void SMDImporter::ParseNodesSection(const char* szCurrent, const char** szCurrentOut) {
+void SMDImporter::ParseNodesSection(const char* szCurrent, const char** szCurrentOut, const char *end) {
     for ( ;; ) {
         // "end\n" - Ends the nodes section
-        if (0 == ASSIMP_strincmp(szCurrent,"end",3) && IsSpaceOrNewLine(*(szCurrent+3))) {
+        if (0 == ASSIMP_strincmp(szCurrent, "end", 3) && IsSpaceOrNewLine(*(szCurrent+3))) {
             szCurrent += 4;
             break;
         }
-        ParseNodeInfo(szCurrent,&szCurrent);
+        ParseNodeInfo(szCurrent,&szCurrent, end);
     }
-    SkipSpacesAndLineEnd(szCurrent,&szCurrent);
+    SkipSpacesAndLineEnd(szCurrent, &szCurrent, end);
     *szCurrentOut = szCurrent;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Parse the triangles section of the file
-void SMDImporter::ParseTrianglesSection(const char* szCurrent, const char** szCurrentOut) {
+void SMDImporter::ParseTrianglesSection(const char *szCurrent, const char **szCurrentOut, const char *end) {
     // Parse a triangle, parse another triangle, parse the next triangle ...
     // and so on until we reach a token that looks quite similar to "end"
     for ( ;; ) {
-        if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) {
+        if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent, end)) {
             break;
         }
 
@@ -750,17 +750,17 @@ void SMDImporter::ParseTrianglesSection(const char* szCurrent, const char** szCu
         if (TokenMatch(szCurrent,"end",3)) {
             break;
         }
-        ParseTriangle(szCurrent,&szCurrent);
+        ParseTriangle(szCurrent,&szCurrent, end);
     }
-    SkipSpacesAndLineEnd(szCurrent,&szCurrent);
+    SkipSpacesAndLineEnd(szCurrent,&szCurrent, end);
     *szCurrentOut = szCurrent;
 }
 // ------------------------------------------------------------------------------------------------
 // Parse the vertex animation section of the file
-void SMDImporter::ParseVASection(const char* szCurrent, const char** szCurrentOut) {
+void SMDImporter::ParseVASection(const char *szCurrent, const char **szCurrentOut, const char *end) {
     unsigned int iCurIndex = 0;
     for ( ;; ) {
-        if (!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) {
+        if (!SkipSpacesAndLineEnd(szCurrent,&szCurrent, end)) {
             break;
         }
 
@@ -774,10 +774,10 @@ void SMDImporter::ParseVASection(const char* szCurrent, const char** szCurrentOu
             // NOTE: The doc says that time values COULD be negative ...
             // NOTE2: this is the shape key -> valve docs
             int iTime = 0;
-            if(!ParseSignedInt(szCurrent,&szCurrent,iTime) || configFrameID != (unsigned int)iTime) {
+            if (!ParseSignedInt(szCurrent, &szCurrent, end, iTime) || configFrameID != (unsigned int)iTime) {
                 break;
             }
-            SkipLine(szCurrent,&szCurrent);
+            SkipLine(szCurrent,&szCurrent, end);
         } else {
             if(0 == iCurIndex) {
                 asTriangles.emplace_back();
@@ -785,7 +785,7 @@ void SMDImporter::ParseVASection(const char* szCurrent, const char** szCurrentOu
             if (++iCurIndex == 3) {
                 iCurIndex = 0;
             }
-            ParseVertex(szCurrent,&szCurrent,asTriangles.back().avVertices[iCurIndex],true);
+            ParseVertex(szCurrent,&szCurrent, end, asTriangles.back().avVertices[iCurIndex],true);
         }
     }
 
@@ -794,16 +794,16 @@ void SMDImporter::ParseVASection(const char* szCurrent, const char** szCurrentOu
         asTriangles.pop_back();
     }
 
-    SkipSpacesAndLineEnd(szCurrent,&szCurrent);
+    SkipSpacesAndLineEnd(szCurrent,&szCurrent, end);
     *szCurrentOut = szCurrent;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Parse the skeleton section of the file
-void SMDImporter::ParseSkeletonSection(const char* szCurrent, const char** szCurrentOut) {
+void SMDImporter::ParseSkeletonSection(const char *szCurrent, const char **szCurrentOut, const char *end) {
     int iTime = 0;
     for ( ;; ) {
-        if (!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) {
+        if (!SkipSpacesAndLineEnd(szCurrent,&szCurrent, end)) {
             break;
         }
 
@@ -811,15 +811,15 @@ void SMDImporter::ParseSkeletonSection(const char* szCurrent, const char** szCur
         if (TokenMatch(szCurrent,"end",3)) {
             break;
         } else if (TokenMatch(szCurrent,"time",4)) {
-        // "time <n>\n" - Specifies the current animation frame
-            if(!ParseSignedInt(szCurrent,&szCurrent,iTime)) {
+            // "time <n>\n" - Specifies the current animation frame
+            if (!ParseSignedInt(szCurrent, &szCurrent, end, iTime)) {
                 break;
             }
 
             iSmallestFrame = std::min(iSmallestFrame,iTime);
-            SkipLine(szCurrent,&szCurrent);
+            SkipLine(szCurrent, &szCurrent, end);
         } else {
-            ParseSkeletonElement(szCurrent,&szCurrent,iTime);
+            ParseSkeletonElement(szCurrent, &szCurrent, end, iTime);
         }
     }
     *szCurrentOut = szCurrent;
@@ -827,16 +827,16 @@ void SMDImporter::ParseSkeletonSection(const char* szCurrent, const char** szCur
 
 // ------------------------------------------------------------------------------------------------
 #define SMDI_PARSE_RETURN { \
-    SkipLine(szCurrent,&szCurrent); \
+    SkipLine(szCurrent,&szCurrent, end); \
     *szCurrentOut = szCurrent; \
     return; \
 }
 // ------------------------------------------------------------------------------------------------
 // Parse a node line
-void SMDImporter::ParseNodeInfo(const char* szCurrent, const char** szCurrentOut) {
+void SMDImporter::ParseNodeInfo(const char *szCurrent, const char **szCurrentOut, const char *end) {
     unsigned int iBone  = 0;
-    SkipSpacesAndLineEnd(szCurrent,&szCurrent);
-    if ( !ParseUnsignedInt(szCurrent,&szCurrent,iBone) || !SkipSpaces(szCurrent,&szCurrent)) {
+    SkipSpacesAndLineEnd(szCurrent, &szCurrent, end);
+    if ( !ParseUnsignedInt(szCurrent, &szCurrent, end, iBone) || !SkipSpaces(szCurrent,&szCurrent, end)) {
         throw DeadlyImportError("Unexpected EOF/EOL while parsing bone index");
     }
     if (iBone == UINT_MAX) {
@@ -877,7 +877,7 @@ void SMDImporter::ParseNodeInfo(const char* szCurrent, const char** szCurrentOut
     szCurrent = szEnd;
 
     // the only negative bone parent index that could occur is -1 AFAIK
-    if(!ParseSignedInt(szCurrent,&szCurrent,(int&)bone.iParent))  {
+    if(!ParseSignedInt(szCurrent, &szCurrent, end, (int&)bone.iParent))  {
         LogErrorNoThrow("Unexpected EOF/EOL while parsing bone parent index. Assuming -1");
         SMDI_PARSE_RETURN;
     }
@@ -888,12 +888,12 @@ void SMDImporter::ParseNodeInfo(const char* szCurrent, const char** szCurrentOut
 
 // ------------------------------------------------------------------------------------------------
 // Parse a skeleton element
-void SMDImporter::ParseSkeletonElement(const char* szCurrent, const char** szCurrentOut,int iTime) {
+void SMDImporter::ParseSkeletonElement(const char *szCurrent, const char **szCurrentOut, const char *end, int iTime) {
     aiVector3D vPos;
     aiVector3D vRot;
 
     unsigned int iBone  = 0;
-    if(!ParseUnsignedInt(szCurrent,&szCurrent,iBone)) {
+    if (!ParseUnsignedInt(szCurrent, &szCurrent, end, iBone)) {
         ASSIMP_LOG_ERROR("Unexpected EOF/EOL while parsing bone index");
         SMDI_PARSE_RETURN;
     }
@@ -907,27 +907,27 @@ void SMDImporter::ParseSkeletonElement(const char* szCurrent, const char** szCur
     SMD::Bone::Animation::MatrixKey& key = bone.sAnim.asKeys.back();
 
     key.dTime = (double)iTime;
-    if(!ParseFloat(szCurrent,&szCurrent,(float&)vPos.x)) {
+    if(!ParseFloat(szCurrent, &szCurrent, end, (float&)vPos.x)) {
         LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.x");
         SMDI_PARSE_RETURN;
     }
-    if(!ParseFloat(szCurrent,&szCurrent,(float&)vPos.y)) {
+    if(!ParseFloat(szCurrent, &szCurrent, end, (float&)vPos.y)) {
         LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.y");
         SMDI_PARSE_RETURN;
     }
-    if(!ParseFloat(szCurrent,&szCurrent,(float&)vPos.z)) {
+    if(!ParseFloat(szCurrent, &szCurrent, end, (float&)vPos.z)) {
         LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.z");
         SMDI_PARSE_RETURN;
     }
-    if(!ParseFloat(szCurrent,&szCurrent,(float&)vRot.x)) {
+    if(!ParseFloat(szCurrent, &szCurrent, end, (float&)vRot.x)) {
         LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.x");
         SMDI_PARSE_RETURN;
     }
-    if(!ParseFloat(szCurrent,&szCurrent,(float&)vRot.y)) {
+    if(!ParseFloat(szCurrent, &szCurrent, end, (float&)vRot.y)) {
         LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.y");
         SMDI_PARSE_RETURN;
     }
-    if(!ParseFloat(szCurrent,&szCurrent,(float&)vRot.z)) {
+    if(!ParseFloat(szCurrent, &szCurrent, end, (float&)vRot.z)) {
         LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.z");
         SMDI_PARSE_RETURN;
     }
@@ -947,11 +947,11 @@ void SMDImporter::ParseSkeletonElement(const char* szCurrent, const char** szCur
 
 // ------------------------------------------------------------------------------------------------
 // Parse a triangle
-void SMDImporter::ParseTriangle(const char* szCurrent, const char** szCurrentOut) {
+void SMDImporter::ParseTriangle(const char *szCurrent, const char **szCurrentOut, const char *end) {
     asTriangles.emplace_back();
     SMD::Face& face = asTriangles.back();
 
-    if(!SkipSpaces(szCurrent,&szCurrent)) {
+    if(!SkipSpaces(szCurrent, &szCurrent, end)) {
         LogErrorNoThrow("Unexpected EOF/EOL while parsing a triangle");
         return;
     }
@@ -963,19 +963,19 @@ void SMDImporter::ParseTriangle(const char* szCurrent, const char** szCurrentOut
     // ... and get the index that belongs to this file name
     face.iTexture = GetTextureIndex(std::string(szLast,(uintptr_t)szCurrent-(uintptr_t)szLast));
 
-    SkipSpacesAndLineEnd(szCurrent,&szCurrent);
+    SkipSpacesAndLineEnd(szCurrent, &szCurrent, end);
 
     // load three vertices
     for (auto &avVertex : face.avVertices) {
-        ParseVertex(szCurrent,&szCurrent, avVertex);
+        ParseVertex(szCurrent, &szCurrent, end, avVertex);
     }
     *szCurrentOut = szCurrent;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Parse a float
-bool SMDImporter::ParseFloat(const char* szCurrent, const char** szCurrentOut, float& out) {
-    if(!SkipSpaces(&szCurrent)) {
+bool SMDImporter::ParseFloat(const char *szCurrent, const char **szCurrentOut, const char *end, float &out) {
+    if (!SkipSpaces(&szCurrent, end)) {
         return false;
     }
 
@@ -985,8 +985,8 @@ bool SMDImporter::ParseFloat(const char* szCurrent, const char** szCurrentOut, f
 
 // ------------------------------------------------------------------------------------------------
 // Parse an unsigned int
-bool SMDImporter::ParseUnsignedInt(const char* szCurrent, const char** szCurrentOut, unsigned int& out) {
-    if(!SkipSpaces(&szCurrent)) {
+bool SMDImporter::ParseUnsignedInt(const char *szCurrent, const char **szCurrentOut, const char *end, unsigned int &out) {
+    if(!SkipSpaces(&szCurrent, end)) {
         return false;
     }
 
@@ -996,8 +996,8 @@ bool SMDImporter::ParseUnsignedInt(const char* szCurrent, const char** szCurrent
 
 // ------------------------------------------------------------------------------------------------
 // Parse a signed int
-bool SMDImporter::ParseSignedInt(const char* szCurrent, const char** szCurrentOut, int& out) {
-    if(!SkipSpaces(&szCurrent)) {
+bool SMDImporter::ParseSignedInt(const char *szCurrent, const char **szCurrentOut, const char *end, int &out) {
+    if(!SkipSpaces(&szCurrent, end)) {
         return false;
     }
 
@@ -1008,37 +1008,37 @@ bool SMDImporter::ParseSignedInt(const char* szCurrent, const char** szCurrentOu
 // ------------------------------------------------------------------------------------------------
 // Parse a vertex
 void SMDImporter::ParseVertex(const char* szCurrent,
-        const char** szCurrentOut, SMD::Vertex& vertex,
+        const char **szCurrentOut, const char *end, SMD::Vertex &vertex,
         bool bVASection /*= false*/) {
-    if (SkipSpaces(&szCurrent) && IsLineEnd(*szCurrent)) {
-        SkipSpacesAndLineEnd(szCurrent,&szCurrent);
-        return ParseVertex(szCurrent,szCurrentOut,vertex,bVASection);
+    if (SkipSpaces(&szCurrent, end) && IsLineEnd(*szCurrent)) {
+        SkipSpacesAndLineEnd(szCurrent,&szCurrent, end);
+        return ParseVertex(szCurrent, szCurrentOut, end, vertex, bVASection);
     }
-    if(!ParseSignedInt(szCurrent,&szCurrent,(int&)vertex.iParentNode)) {
+    if(!ParseSignedInt(szCurrent, &szCurrent, end, (int&)vertex.iParentNode)) {
         LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.parent");
         SMDI_PARSE_RETURN;
     }
-    if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.x)) {
+    if(!ParseFloat(szCurrent, &szCurrent, end, (float&)vertex.pos.x)) {
         LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.x");
         SMDI_PARSE_RETURN;
     }
-    if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.y)) {
+    if(!ParseFloat(szCurrent, &szCurrent, end, (float&)vertex.pos.y)) {
         LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.y");
         SMDI_PARSE_RETURN;
     }
-    if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.z)) {
+    if(!ParseFloat(szCurrent, &szCurrent, end, (float&)vertex.pos.z)) {
         LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.z");
         SMDI_PARSE_RETURN;
     }
-    if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.x)) {
+    if(!ParseFloat(szCurrent,&szCurrent,end, (float&)vertex.nor.x)) {
         LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.x");
         SMDI_PARSE_RETURN;
     }
-    if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.y)) {
+    if(!ParseFloat(szCurrent,&szCurrent, end, (float&)vertex.nor.y)) {
         LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.y");
         SMDI_PARSE_RETURN;
     }
-    if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.z)) {
+    if(!ParseFloat(szCurrent, &szCurrent, end, (float&)vertex.nor.z)) {
         LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.z");
         SMDI_PARSE_RETURN;
     }
@@ -1047,11 +1047,11 @@ void SMDImporter::ParseVertex(const char* szCurrent,
         SMDI_PARSE_RETURN;
     }
 
-    if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.uv.x)) {
+    if(!ParseFloat(szCurrent, &szCurrent, end, (float&)vertex.uv.x)) {
         LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.uv.x");
         SMDI_PARSE_RETURN;
     }
-    if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.uv.y)) {
+    if(!ParseFloat(szCurrent, &szCurrent, end, (float&)vertex.uv.y)) {
         LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.uv.y");
         SMDI_PARSE_RETURN;
     }
@@ -1059,16 +1059,16 @@ void SMDImporter::ParseVertex(const char* szCurrent,
     // now read the number of bones affecting this vertex
     // all elements from now are fully optional, we don't need them
     unsigned int iSize = 0;
-    if(!ParseUnsignedInt(szCurrent,&szCurrent,iSize)) {
+    if(!ParseUnsignedInt(szCurrent, &szCurrent, end, iSize)) {
         SMDI_PARSE_RETURN;
     }
     vertex.aiBoneLinks.resize(iSize,std::pair<unsigned int, float>(0,0.0f));
 
     for (auto &aiBoneLink : vertex.aiBoneLinks) {
-        if(!ParseUnsignedInt(szCurrent,&szCurrent,aiBoneLink.first)) {
+        if(!ParseUnsignedInt(szCurrent, &szCurrent, end, aiBoneLink.first)) {
             SMDI_PARSE_RETURN;
         }
-        if(!ParseFloat(szCurrent,&szCurrent,aiBoneLink.second)) {
+        if(!ParseFloat(szCurrent, &szCurrent, end, aiBoneLink.second)) {
             SMDI_PARSE_RETURN;
         }
     }
@@ -1077,6 +1077,6 @@ void SMDImporter::ParseVertex(const char* szCurrent,
     SMDI_PARSE_RETURN;
 }
 
-}
+} // namespace Assimp
 
 #endif // !! ASSIMP_BUILD_NO_SMD_IMPORTER

+ 18 - 21
code/AssetLib/SMD/SMDLoader.h

@@ -162,7 +162,7 @@ struct Bone {
 class ASSIMP_API SMDImporter : public BaseImporter {
 public:
     SMDImporter();
-    ~SMDImporter() override;
+    ~SMDImporter() override = default;
 
     // -------------------------------------------------------------------
     /** Returns whether the class can handle the format of the given file.
@@ -206,7 +206,7 @@ protected:
      * the next section (or to EOF)
     */
     void ParseTrianglesSection(const char* szCurrent,
-        const char** szCurrentOut);
+            const char **szCurrentOut, const char *end);
 
     // -------------------------------------------------------------------
     /** Parse the vertex animation section in VTA files
@@ -216,7 +216,7 @@ protected:
      * the next section (or to EOF)
     */
     void ParseVASection(const char* szCurrent,
-        const char** szCurrentOut);
+            const char **szCurrentOu, const char *end);
 
     // -------------------------------------------------------------------
     /** Parse the nodes section of the SMD file
@@ -226,7 +226,7 @@ protected:
      * the next section (or to EOF)
     */
     void ParseNodesSection(const char* szCurrent,
-        const char** szCurrentOut);
+            const char **szCurrentOut, const char *end);
 
     // -------------------------------------------------------------------
     /** Parse the skeleton section of the SMD file
@@ -236,7 +236,7 @@ protected:
      * the next section (or to EOF)
     */
     void ParseSkeletonSection(const char* szCurrent,
-        const char** szCurrentOut);
+            const char **szCurrentOut, const char *end);
 
     // -------------------------------------------------------------------
     /** Parse a single triangle in the SMD file
@@ -245,8 +245,7 @@ protected:
      * \param szCurrentOut Receives the output cursor position
     */
     void ParseTriangle(const char* szCurrent,
-        const char** szCurrentOut);
-
+            const char **szCurrentOut, const char *end);
 
     // -------------------------------------------------------------------
     /** Parse a single vertex in the SMD file
@@ -256,7 +255,7 @@ protected:
      * \param vertex Vertex to be filled
     */
     void ParseVertex(const char* szCurrent,
-        const char** szCurrentOut, SMD::Vertex& vertex,
+            const char **szCurrentOut, const char *end, SMD::Vertex &vertex,
         bool bVASection = false);
 
     // -------------------------------------------------------------------
@@ -271,32 +270,31 @@ protected:
     /** Parse a line in the skeleton section
      */
     void ParseSkeletonElement(const char* szCurrent,
-        const char** szCurrentOut,int iTime);
+            const char **szCurrentOut, const char *end, int iTime);
 
     // -------------------------------------------------------------------
     /** Parse a line in the nodes section
      */
     void ParseNodeInfo(const char* szCurrent,
-        const char** szCurrentOut);
-
+            const char **szCurrentOut, const char *end);
 
     // -------------------------------------------------------------------
     /** Parse a floating-point value
      */
     bool ParseFloat(const char* szCurrent,
-        const char** szCurrentOut, float& out);
+            const char **szCurrentOut, const char *end, float &out);
 
     // -------------------------------------------------------------------
     /** Parse an unsigned integer. There may be no sign!
      */
     bool ParseUnsignedInt(const char* szCurrent,
-        const char** szCurrentOut, unsigned int& out);
+            const char **szCurrentOut, const char *end, unsigned int &out);
 
     // -------------------------------------------------------------------
     /** Parse a signed integer. Signs (+,-) are handled.
      */
     bool ParseSignedInt(const char* szCurrent,
-        const char** szCurrentOut, int& out);
+            const char **szCurrentOut, const char *end, int &out);
 
     // -------------------------------------------------------------------
     /** Fix invalid time values in the file
@@ -304,7 +302,7 @@ protected:
     void FixTimeValues();
 
     // -------------------------------------------------------------------
-    /** Add all children of a bone as subnodes to a node
+    /** Add all children of a bone as sub-nodes to a node
      * \param pcNode Parent node
      * \param iParent Parent bone index
      */
@@ -329,17 +327,15 @@ protected:
 
 
     // -------------------------------------------------------------------
-    inline bool SkipLine( const char* in, const char** out)
-    {
-        Assimp::SkipLine(in,out);
+    inline bool SkipLine( const char* in, const char** out, const char *end) {
+        Assimp::SkipLine(in, out, end);
         ++iLineNumber;
         return true;
     }
     // -------------------------------------------------------------------
-    inline bool SkipSpacesAndLineEnd( const char* in, const char** out)
-    {
+    inline bool SkipSpacesAndLineEnd(const char *in, const char **out, const char *end) {
         ++iLineNumber;
-        return Assimp::SkipSpacesAndLineEnd(in,out);
+        return Assimp::SkipSpacesAndLineEnd(in, out, end);
     }
 
 private:
@@ -349,6 +345,7 @@ private:
 
     /** Buffer to hold the loaded file */
     std::vector<char> mBuffer;
+    char *mEnd;
 
     /** Output scene to be filled
     */

+ 28 - 31
code/AssetLib/STEPParser/STEPFileReader.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -40,9 +39,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ----------------------------------------------------------------------
 */
 
-/** @file  STEPFileReader.cpp
+/** 
+ * @file  STEPFileReader.cpp
  *  @brief Implementation of the STEP file parser, which fills a
- *     STEP::DB with data read from a file.
+ *         STEP::DB with data read from a file.
  */
 
 #include "STEPFileReader.h"
@@ -58,34 +58,28 @@ using namespace Assimp;
 namespace EXPRESS = STEP::EXPRESS;
 
 // ------------------------------------------------------------------------------------------------
-std::string AddLineNumber(const std::string& s,uint64_t line /*= LINE_NOT_SPECIFIED*/, const std::string& prefix = std::string())
-{
+std::string AddLineNumber(const std::string& s,uint64_t line /*= LINE_NOT_SPECIFIED*/, const std::string& prefix = std::string()) {
     return line == STEP::SyntaxError::LINE_NOT_SPECIFIED ? prefix+s : static_cast<std::string>( (Formatter::format(),prefix,"(line ",line,") ",s) );
 }
 
 // ------------------------------------------------------------------------------------------------
-std::string AddEntityID(const std::string& s,uint64_t entity /*= ENTITY_NOT_SPECIFIED*/, const std::string& prefix = std::string())
-{
+std::string AddEntityID(const std::string& s,uint64_t entity /*= ENTITY_NOT_SPECIFIED*/, const std::string& prefix = std::string()) {
     return entity == STEP::TypeError::ENTITY_NOT_SPECIFIED ? prefix+s : static_cast<std::string>( (Formatter::format(),prefix,"(entity #",entity,") ",s));
 }
 
 
 // ------------------------------------------------------------------------------------------------
-STEP::SyntaxError::SyntaxError (const std::string& s,uint64_t line /* = LINE_NOT_SPECIFIED */)
-: DeadlyImportError(AddLineNumber(s,line))
-{
-
+STEP::SyntaxError::SyntaxError (const std::string& s,uint64_t line) : DeadlyImportError(AddLineNumber(s,line)) {
+    // empty
 }
 
 // ------------------------------------------------------------------------------------------------
-STEP::TypeError::TypeError (const std::string& s,uint64_t entity /* = ENTITY_NOT_SPECIFIED */,uint64_t line /*= LINE_NOT_SPECIFIED*/)
-: DeadlyImportError(AddLineNumber(AddEntityID(s,entity),line))
-{
-
+STEP::TypeError::TypeError (const std::string& s,uint64_t entity, uint64_t line) : DeadlyImportError(AddLineNumber(AddEntityID(s,entity),line)) {
+    // empty
 }
 
-static const char *ISO_Token         = "ISO-10303-21;";
-static const char *FILE_SCHEMA_Token = "FILE_SCHEMA";
+static constexpr char ISO_Token[]         = "ISO-10303-21;";
+static constexpr char FILE_SCHEMA_Token[] = "FILE_SCHEMA";
 // ------------------------------------------------------------------------------------------------
 STEP::DB* STEP::ReadFileHeader(std::shared_ptr<IOStream> stream) {
     std::shared_ptr<StreamReaderLE> reader = std::shared_ptr<StreamReaderLE>(new StreamReaderLE(std::move(stream)));
@@ -110,8 +104,9 @@ STEP::DB* STEP::ReadFileHeader(std::shared_ptr<IOStream> stream) {
 
         if (s.substr(0,11) == FILE_SCHEMA_Token) {
             const char* sz = s.c_str()+11;
-            SkipSpaces(sz,&sz);
-            std::shared_ptr< const EXPRESS::DataType > schema = EXPRESS::DataType::Parse(sz);
+            const char *end = s.c_str() + s.size();
+            SkipSpaces(sz,&sz, end);
+            std::shared_ptr< const EXPRESS::DataType > schema = EXPRESS::DataType::Parse(sz, end);
 
             // the file schema should be a regular list entity, although it usually contains exactly one entry
             // since the list itself is contained in a regular parameter list, we actually have
@@ -304,10 +299,10 @@ void STEP::ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme,
 }
 
 // ------------------------------------------------------------------------------------------------
-std::shared_ptr<const EXPRESS::DataType> EXPRESS::DataType::Parse(const char*& inout,uint64_t line, const EXPRESS::ConversionSchema* schema /*= nullptr*/)
+std::shared_ptr<const EXPRESS::DataType> EXPRESS::DataType::Parse(const char*& inout, const char *end, uint64_t line, const EXPRESS::ConversionSchema* schema /*= nullptr*/)
 {
     const char* cur = inout;
-    SkipSpaces(&cur);
+    SkipSpaces(&cur, end);
     if (*cur == ',' || IsSpaceOrNewLine(*cur)) {
         throw STEP::SyntaxError("unexpected token, expected parameter",line);
     }
@@ -325,7 +320,7 @@ std::shared_ptr<const EXPRESS::DataType> EXPRESS::DataType::Parse(const char*& i
                 std::transform(s.begin(),s.end(),s.begin(),&ai_tolower<char> );
                 if (schema->IsKnownToken(s)) {
                     for(cur = t+1;*cur++ != '(';);
-                    std::shared_ptr<const EXPRESS::DataType> dt = Parse(cur);
+                    std::shared_ptr<const EXPRESS::DataType> dt = Parse(cur, end);
                     inout = *cur ? cur+1 : cur;
                     return dt;
                 }
@@ -348,7 +343,7 @@ std::shared_ptr<const EXPRESS::DataType> EXPRESS::DataType::Parse(const char*& i
     else if (*cur == '(' ) {
         // start of an aggregate, further parsing is done by the LIST factory constructor
         inout = cur;
-        return EXPRESS::LIST::Parse(inout,line,schema);
+        return EXPRESS::LIST::Parse(inout, end, line, schema);
     }
     else if (*cur == '.' ) {
         // enum (includes boolean)
@@ -427,9 +422,10 @@ std::shared_ptr<const EXPRESS::DataType> EXPRESS::DataType::Parse(const char*& i
 }
 
 // ------------------------------------------------------------------------------------------------
-std::shared_ptr<const EXPRESS::LIST> EXPRESS::LIST::Parse(const char*& inout,uint64_t line, const EXPRESS::ConversionSchema* schema /*= nullptr*/) {
+std::shared_ptr<const EXPRESS::LIST> EXPRESS::LIST::Parse(const char*& inout, const char *end,
+        uint64_t line, const EXPRESS::ConversionSchema* schema) {
     const std::shared_ptr<EXPRESS::LIST> list = std::make_shared<EXPRESS::LIST>();
-    EXPRESS::LIST::MemberList& members = list->members;
+    EXPRESS::LIST::MemberList& cur_members = list->members;
 
     const char* cur = inout;
     if (*cur++ != '(') {
@@ -442,19 +438,19 @@ std::shared_ptr<const EXPRESS::LIST> EXPRESS::LIST::Parse(const char*& inout,uin
         count += (*c == ',' ? 1 : 0);
     }
 
-    members.reserve(count);
+    cur_members.reserve(count);
 
     for(;;++cur) {
         if (!*cur) {
             throw STEP::SyntaxError("unexpected end of line while reading list");
         }
-        SkipSpaces(cur,&cur);
+        SkipSpaces(cur,&cur, end);
         if (*cur == ')') {
             break;
         }
 
-        members.push_back( EXPRESS::DataType::Parse(cur,line,schema));
-        SkipSpaces(cur,&cur);
+        cur_members.push_back(EXPRESS::DataType::Parse(cur, end, line, schema));
+        SkipSpaces(cur, &cur, end);
 
         if (*cur != ',') {
             if (*cur == ')') {
@@ -464,7 +460,7 @@ std::shared_ptr<const EXPRESS::LIST> EXPRESS::LIST::Parse(const char*& inout,uin
         }
     }
 
-    inout = cur+1;
+    inout = cur + 1;
     return list;
 }
 
@@ -543,7 +539,8 @@ void STEP::LazyObject::LazyInit() const {
     }
 
     const char* acopy = args;
-    std::shared_ptr<const EXPRESS::LIST> conv_args = EXPRESS::LIST::Parse(acopy,(uint64_t)STEP::SyntaxError::LINE_NOT_SPECIFIED,&db.GetSchema());
+    const char *end = acopy + std::strlen(args);
+    std::shared_ptr<const EXPRESS::LIST> conv_args = EXPRESS::LIST::Parse(acopy, end, (uint64_t)STEP::SyntaxError::LINE_NOT_SPECIFIED,&db.GetSchema());
     delete[] args;
     args = nullptr;
 

+ 1 - 2
code/AssetLib/STEPParser/STEPFileReader.h

@@ -60,8 +60,7 @@ void ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme, const char* const*
 
 /// @brief  Helper to read a file.
 template <size_t N, size_t N2>
-inline
-void ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme, const char* const (&arr)[N], const char* const (&arr2)[N2]) {
+inline void ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme, const char* const (&arr)[N], const char* const (&arr2)[N2]) {
     return ReadFile(db,scheme,arr,N,arr2,N2);
 }
 

+ 12 - 12
code/AssetLib/STL/STLLoader.cpp

@@ -98,7 +98,7 @@ static bool IsAsciiSTL(const char *buffer, size_t fileSize) {
 
     const char *bufferEnd = buffer + fileSize;
 
-    if (!SkipSpaces(&buffer)) {
+    if (!SkipSpaces(&buffer, bufferEnd)) {
         return false;
     }
 
@@ -244,11 +244,11 @@ void STLImporter::LoadASCIIFile(aiNode *root) {
         aiNode *node = new aiNode;
         node->mParent = root;
         nodes.push_back(node);
-        SkipSpaces(&sz);
+        SkipSpaces(&sz, bufferEnd);
         ai_assert(!IsLineEnd(sz));
 
         sz += 5; // skip the "solid"
-        SkipSpaces(&sz);
+        SkipSpaces(&sz, bufferEnd);
         const char *szMe = sz;
         while (!IsSpaceOrNewLine(*sz)) {
             sz++;
@@ -270,7 +270,7 @@ void STLImporter::LoadASCIIFile(aiNode *root) {
         unsigned int faceVertexCounter = 3;
         for (;;) {
             // go to the next token
-            if (!SkipSpacesAndLineEnd(&sz)) {
+            if (!SkipSpacesAndLineEnd(&sz, bufferEnd)) {
                 // seems we're finished although there was no end marker
                 ASSIMP_LOG_WARN("STL: unexpected EOF. \'endsolid\' keyword was expected");
                 break;
@@ -284,7 +284,7 @@ void STLImporter::LoadASCIIFile(aiNode *root) {
                 faceVertexCounter = 0;
 
                 sz += 6;
-                SkipSpaces(&sz);
+                SkipSpaces(&sz, bufferEnd);
                 if (strncmp(sz, "normal", 6)) {
                     ASSIMP_LOG_WARN("STL: a facet normal vector was expected but not found");
                 } else {
@@ -293,11 +293,11 @@ void STLImporter::LoadASCIIFile(aiNode *root) {
                     }
                     aiVector3D vn;
                     sz += 7;
-                    SkipSpaces(&sz);
+                    SkipSpaces(&sz, bufferEnd);
                     sz = fast_atoreal_move<ai_real>(sz, (ai_real &)vn.x);
-                    SkipSpaces(&sz);
+                    SkipSpaces(&sz, bufferEnd);
                     sz = fast_atoreal_move<ai_real>(sz, (ai_real &)vn.y);
-                    SkipSpaces(&sz);
+                    SkipSpaces(&sz, bufferEnd);
                     sz = fast_atoreal_move<ai_real>(sz, (ai_real &)vn.z);
                     normalBuffer.emplace_back(vn);
                     normalBuffer.emplace_back(vn);
@@ -312,13 +312,13 @@ void STLImporter::LoadASCIIFile(aiNode *root) {
                         throw DeadlyImportError("STL: unexpected EOF while parsing facet");
                     }
                     sz += 7;
-                    SkipSpaces(&sz);
+                    SkipSpaces(&sz, bufferEnd);
                     positionBuffer.emplace_back();
                     aiVector3D *vn = &positionBuffer.back();
                     sz = fast_atoreal_move<ai_real>(sz, (ai_real &)vn->x);
-                    SkipSpaces(&sz);
+                    SkipSpaces(&sz, bufferEnd);
                     sz = fast_atoreal_move<ai_real>(sz, (ai_real &)vn->y);
-                    SkipSpaces(&sz);
+                    SkipSpaces(&sz, bufferEnd);
                     sz = fast_atoreal_move<ai_real>(sz, (ai_real &)vn->z);
                     faceVertexCounter++;
                 }
@@ -326,7 +326,7 @@ void STLImporter::LoadASCIIFile(aiNode *root) {
                 do {
                     ++sz;
                 } while (!IsLineEnd(*sz));
-                SkipSpacesAndLineEnd(&sz);
+                SkipSpacesAndLineEnd(&sz, bufferEnd);
                 // finished!
                 break;
             } else { // else skip the whole identifier

+ 12 - 20
code/AssetLib/Step/STEPFile.h

@@ -199,35 +199,27 @@ public:
     }
 
 public:
-    /** parse a variable from a string and set 'inout' to the character
-     *  behind the last consumed character. An optional schema enables,
-     *  if specified, automatic conversion of custom data types.
-     *
-     *  @throw SyntaxError
-     */
-    static std::shared_ptr<const EXPRESS::DataType> Parse(const char *&inout,
-            uint64_t line = SyntaxError::LINE_NOT_SPECIFIED,
-            const EXPRESS::ConversionSchema *schema = nullptr);
+    /// @brief Parse a variable from a string and set 'inout' to the character behind the last consumed character.
+    ///
+    ///  An optional schema enables, if specified, automatic conversion of custom data types.
+    ///
+    /// @throw SyntaxError
+    static std::shared_ptr<const EXPRESS::DataType> Parse(const char *&inout, const char *end,
+            uint64_t line = SyntaxError::LINE_NOT_SPECIFIED, const EXPRESS::ConversionSchema *schema = nullptr);
 };
 
 typedef DataType SELECT;
 typedef DataType LOGICAL;
 
 // -------------------------------------------------------------------------------
-/** Sentinel class to represent explicitly unset (optional) fields ($) */
+/// Sentinel class to represent explicitly unset (optional) fields ($)
 // -------------------------------------------------------------------------------
-class UNSET : public DataType {
-public:
-private:
-};
+class UNSET : public DataType {};
 
 // -------------------------------------------------------------------------------
-/** Sentinel class to represent explicitly derived fields (*) */
+/// Sentinel class to represent explicitly derived fields (*)
 // -------------------------------------------------------------------------------
-class ISDERIVED : public DataType {
-public:
-private:
-};
+class ISDERIVED : public DataType {};
 
 // -------------------------------------------------------------------------------
 /** Shared implementation for some of the primitive data type, i.e. int, float
@@ -304,7 +296,7 @@ public:
 public:
     /** @see DaraType::Parse
      */
-    static std::shared_ptr<const EXPRESS::LIST> Parse(const char *&inout,
+    static std::shared_ptr<const EXPRESS::LIST> Parse(const char *&inout, const char *end,
             uint64_t line = SyntaxError::LINE_NOT_SPECIFIED,
             const EXPRESS::ConversionSchema *schema = nullptr);
 

+ 8 - 7
code/AssetLib/Unreal/UnrealLoader.cpp

@@ -320,17 +320,18 @@ void UnrealImporter::InternReadFile(const std::string &pFile,
         std::vector<char> _data;
         TextFileToBuffer(pb.get(), _data);
         const char *data = &_data[0];
+        const char *end = &_data[_data.size() - 1] + 1;
 
         std::vector<std::pair<std::string, std::string>> tempTextures;
 
         // do a quick search in the UC file for some known, usually texture-related, tags
         for (; *data; ++data) {
             if (TokenMatchI(data, "#exec", 5)) {
-                SkipSpacesAndLineEnd(&data);
+                SkipSpacesAndLineEnd(&data, end);
 
                 // #exec TEXTURE IMPORT [...] NAME=jjjjj [...] FILE=jjjj.pcx [...]
                 if (TokenMatchI(data, "TEXTURE", 7)) {
-                    SkipSpacesAndLineEnd(&data);
+                    SkipSpacesAndLineEnd(&data, end);
 
                     if (TokenMatchI(data, "IMPORT", 6)) {
                         tempTextures.emplace_back();
@@ -348,14 +349,15 @@ void UnrealImporter::InternReadFile(const std::string &pFile,
                                 me.second = std::string(d, (size_t)(data - d));
                             }
                         }
-                        if (!me.first.length() || !me.second.length())
+                        if (!me.first.length() || !me.second.length()) {
                             tempTextures.pop_back();
+                        }
                     }
                 }
                 // #exec MESHMAP SETTEXTURE MESHMAP=box NUM=1 TEXTURE=Jtex1
                 // #exec MESHMAP SCALE MESHMAP=box X=0.1 Y=0.1 Z=0.2
                 else if (TokenMatchI(data, "MESHMAP", 7)) {
-                    SkipSpacesAndLineEnd(&data);
+                    SkipSpacesAndLineEnd(&data, end);
 
                     if (TokenMatchI(data, "SETTEXTURE", 10)) {
 
@@ -369,8 +371,7 @@ void UnrealImporter::InternReadFile(const std::string &pFile,
                             } else if (!ASSIMP_strincmp(data, "TEXTURE=", 8)) {
                                 data += 8;
                                 const char *d = data;
-                                for (; !IsSpaceOrNewLine(*data); ++data)
-                                    ;
+                                for (; !IsSpaceOrNewLine(*data); ++data);
                                 me.second = std::string(d, (size_t)(data - d));
 
                                 // try to find matching path names, doesn't care if we don't find them
@@ -408,7 +409,7 @@ void UnrealImporter::InternReadFile(const std::string &pFile,
     // find out how many output meshes and materials we'll have and build material indices
     for (Unreal::Triangle &tri : triangles) {
         Unreal::TempMat mat(tri);
-        std::vector<Unreal::TempMat>::iterator nt = std::find(materials.begin(), materials.end(), mat);
+        auto nt = std::find(materials.begin(), materials.end(), mat);
         if (nt == materials.end()) {
             // add material
             tri.matIndex = static_cast<unsigned int>(materials.size());

+ 13 - 9
code/AssetLib/XGL/XGLLoader.cpp

@@ -700,13 +700,14 @@ unsigned int XGLImporter::ReadIDAttr(XmlNode &node) {
 float XGLImporter::ReadFloat(XmlNode &node) {
     std::string v;
     XmlParser::getValueAsString(node, v);
-    const char *s = v.c_str(), *se;
-	if (!SkipSpaces(&s)) {
+    const char *s = v.c_str();
+    const char *end = v.c_str() + v.size();
+	if (!SkipSpaces(&s, end)) {
 		LogError("unexpected EOL, failed to parse index element");
 		return 0.f;
 	}
-	float t;
-	se = fast_atoreal_move(s, t);
+    float t{ 0.0f };
+	const char *se = fast_atoreal_move(s, t);
 	if (se == s) {
 		LogError("failed to read float text");
 		return 0.f;
@@ -720,7 +721,8 @@ unsigned int XGLImporter::ReadIndexFromText(XmlNode &node) {
     std::string v;
     XmlParser::getValueAsString(node, v);
     const char *s = v.c_str();
-	if (!SkipSpaces(&s)) {
+    const char *end = v.c_str() + v.size();
+    if (!SkipSpaces(&s, end)) {
 		LogError("unexpected EOL, failed to parse index element");
         return ErrorId;
 	}
@@ -741,16 +743,17 @@ aiVector2D XGLImporter::ReadVec2(XmlNode &node) {
     std::string val;
     XmlParser::getValueAsString(node, val);
     const char *s = val.c_str();
+    const char *end = val.c_str() + val.size();
     ai_real v[2] = {};
 	for (int i = 0; i < 2; ++i) {
-		if (!SkipSpaces(&s)) {
+		if (!SkipSpaces(&s, end)) {
 			LogError("unexpected EOL, failed to parse vec2");
 			return vec;
 		}
 
 		v[i] = fast_atof(&s);
 
-		SkipSpaces(&s);
+		SkipSpaces(&s, end);
 		if (i != 1 && *s != ',') {
 			LogError("expected comma, failed to parse vec2");
 			return vec;
@@ -769,14 +772,15 @@ aiVector3D XGLImporter::ReadVec3(XmlNode &node) {
     std::string v;
     XmlParser::getValueAsString(node, v);
 	const char *s = v.c_str();
+    const char *end = v.c_str() + v.size();
 	for (int i = 0; i < 3; ++i) {
-		if (!SkipSpaces(&s)) {
+		if (!SkipSpaces(&s, end)) {
 			LogError("unexpected EOL, failed to parse vec3");
 			return vec;
 		}
 		vec[i] = fast_atof(&s);
 
-		SkipSpaces(&s);
+		SkipSpaces(&s, end);
 		if (i != 2 && *s != ',') {
 			LogError("expected comma, failed to parse vec3");
 			return vec;

+ 4 - 0
code/CMakeLists.txt

@@ -1193,6 +1193,10 @@ ENDIF ()
 
 TARGET_USE_COMMON_OUTPUT_DIRECTORY(assimp)
 
+add_compile_options(
+  "$<$<CONFIG:DEBUG>:-O0;-g3;-ggdb>"
+)
+
 IF (ASSIMP_WARNINGS_AS_ERRORS)
   MESSAGE(STATUS "Treating all warnings as errors (for assimp library only)")
   IF (MSVC)

+ 3 - 2
code/PostProcessing/ProcessHelper.cpp

@@ -52,8 +52,9 @@ namespace Assimp {
 // -------------------------------------------------------------------------------
 void ConvertListToStrings(const std::string &in, std::list<std::string> &out) {
     const char *s = in.c_str();
+    const char *end = in.c_str() + in.size();
     while (*s) {
-        SkipSpacesAndLineEnd(&s);
+        SkipSpacesAndLineEnd(&s, end);
         if (*s == '\'') {
             const char *base = ++s;
             while (*s != '\'') {
@@ -66,7 +67,7 @@ void ConvertListToStrings(const std::string &in, std::list<std::string> &out) {
             out.emplace_back(base, (size_t)(s - base));
             ++s;
         } else {
-            out.push_back(GetNextToken(s));
+            out.push_back(GetNextToken(s, end));
         }
     }
 }

+ 14 - 4
include/assimp/LineSplitter.h

@@ -110,6 +110,8 @@ public:
 
     std::string operator* () const;
 
+    const char *getEnd() const;
+
     // -----------------------------------------
     /** boolean context */
     operator bool() const;
@@ -139,17 +141,21 @@ public:
 private:
     line_idx mIdx;
     std::string mCur;
+    const char *mEnd;
     StreamReaderLE& mStream;
     bool mSwallow, mSkip_empty_lines, mTrim;
 };
 
 AI_FORCE_INLINE LineSplitter::LineSplitter(StreamReaderLE& stream, bool skip_empty_lines, bool trim ) :
         mIdx(0),
+        mCur(),
+        mEnd(nullptr),
         mStream(stream),
         mSwallow(),
         mSkip_empty_lines(skip_empty_lines),
         mTrim(trim) {
     mCur.reserve(1024);
+    mEnd = mCur.c_str() + 1024;
     operator++();
     mIdx = 0;
 }
@@ -203,14 +209,14 @@ AI_FORCE_INLINE LineSplitter &LineSplitter::operator++(int) {
 AI_FORCE_INLINE const char *LineSplitter::operator[] (size_t idx) const {
     const char* s = operator->()->c_str();
 
-    SkipSpaces(&s);
+    SkipSpaces(&s, mEnd);
     for (size_t i = 0; i < idx; ++i) {
         for (; !IsSpace(*s); ++s) {
             if (IsLineEnd(*s)) {
                 throw std::range_error("Token index out of range, EOL reached");
             }
         }
-        SkipSpaces(&s);
+        SkipSpaces(&s, mEnd);
     }
     return s;
 }
@@ -219,7 +225,7 @@ template <size_t N>
 AI_FORCE_INLINE void LineSplitter::get_tokens(const char* (&tokens)[N]) const {
     const char* s = operator->()->c_str();
 
-    SkipSpaces(&s);
+    SkipSpaces(&s, mEnd);
     for (size_t i = 0; i < N; ++i) {
         if (IsLineEnd(*s)) {
             throw std::range_error("Token count out of range, EOL reached");
@@ -227,7 +233,7 @@ AI_FORCE_INLINE void LineSplitter::get_tokens(const char* (&tokens)[N]) const {
         tokens[i] = s;
 
         for (; *s && !IsSpace(*s); ++s);
-        SkipSpaces(&s);
+        SkipSpaces(&s, mEnd);
     }
 }
 
@@ -239,6 +245,10 @@ AI_FORCE_INLINE std::string LineSplitter::operator* () const {
     return mCur;
 }
 
+AI_FORCE_INLINE const char* LineSplitter::getEnd() const {
+    return mEnd;
+}
+
 AI_FORCE_INLINE LineSplitter::operator bool() const {
     return mStream.GetRemainingSize() > 0;
 }

+ 17 - 17
include/assimp/ParsingUtils.h

@@ -102,8 +102,8 @@ AI_FORCE_INLINE bool IsSpaceOrNewLine(char_t in) {
 
 // ---------------------------------------------------------------------------------
 template <class char_t>
-AI_FORCE_INLINE bool SkipSpaces(const char_t *in, const char_t **out) {
-    while (*in == (char_t)' ' || *in == (char_t)'\t') {
+AI_FORCE_INLINE bool SkipSpaces(const char_t *in, const char_t **out, const char_t *end) {
+    while ((*in == (char_t)' ' || *in == (char_t)'\t') && in != end) {
         ++in;
     }
     *out = in;
@@ -112,19 +112,19 @@ AI_FORCE_INLINE bool SkipSpaces(const char_t *in, const char_t **out) {
 
 // ---------------------------------------------------------------------------------
 template <class char_t>
-AI_FORCE_INLINE bool SkipSpaces(const char_t **inout) {
-    return SkipSpaces<char_t>(*inout, inout);
+AI_FORCE_INLINE bool SkipSpaces(const char_t **inout, const char_t *end) {
+    return SkipSpaces<char_t>(*inout, inout, end);
 }
 
 // ---------------------------------------------------------------------------------
 template <class char_t>
-AI_FORCE_INLINE bool SkipLine(const char_t *in, const char_t **out) {
-    while (*in != (char_t)'\r' && *in != (char_t)'\n' && *in != (char_t)'\0') {
+AI_FORCE_INLINE bool SkipLine(const char_t *in, const char_t **out, const char_t *end) {
+    while ((*in != (char_t)'\r' && *in != (char_t)'\n' && *in != (char_t)'\0') && in != end) {
         ++in;
     }
 
     // files are opened in binary mode. Ergo there are both NL and CR
-    while (*in == (char_t)'\r' || *in == (char_t)'\n') {
+    while ((*in == (char_t)'\r' || *in == (char_t)'\n') && in != end) {
         ++in;
     }
     *out = in;
@@ -133,14 +133,14 @@ AI_FORCE_INLINE bool SkipLine(const char_t *in, const char_t **out) {
 
 // ---------------------------------------------------------------------------------
 template <class char_t>
-AI_FORCE_INLINE bool SkipLine(const char_t **inout) {
-    return SkipLine<char_t>(*inout, inout);
+AI_FORCE_INLINE bool SkipLine(const char_t **inout, const char_t *end) {
+    return SkipLine<char_t>(*inout, inout, end);
 }
 
 // ---------------------------------------------------------------------------------
 template <class char_t>
-AI_FORCE_INLINE bool SkipSpacesAndLineEnd(const char_t *in, const char_t **out) {
-    while (*in == (char_t)' ' || *in == (char_t)'\t' || *in == (char_t)'\r' || *in == (char_t)'\n') {
+AI_FORCE_INLINE bool SkipSpacesAndLineEnd(const char_t *in, const char_t **out, const char_t  *end) {
+    while ((*in == (char_t)' ' || *in == (char_t)'\t' || *in == (char_t)'\r' || *in == (char_t)'\n') && in != end) {
         ++in;
     }
     *out = in;
@@ -149,8 +149,8 @@ AI_FORCE_INLINE bool SkipSpacesAndLineEnd(const char_t *in, const char_t **out)
 
 // ---------------------------------------------------------------------------------
 template <class char_t>
-AI_FORCE_INLINE bool SkipSpacesAndLineEnd(const char_t **inout) {
-    return SkipSpacesAndLineEnd<char_t>(*inout, inout);
+AI_FORCE_INLINE bool SkipSpacesAndLineEnd(const char_t **inout, const char_t *end) {
+    return SkipSpacesAndLineEnd<char_t>(*inout, inout, end);
 }
 
 // ---------------------------------------------------------------------------------
@@ -210,16 +210,16 @@ AI_FORCE_INLINE bool TokenMatchI(const char *&in, const char *token, unsigned in
 }
 
 // ---------------------------------------------------------------------------------
-AI_FORCE_INLINE void SkipToken(const char *&in) {
-    SkipSpaces(&in);
+AI_FORCE_INLINE void SkipToken(const char *&in, const char *end) {
+    SkipSpaces(&in, end);
     while (!IsSpaceOrNewLine(*in)) {
         ++in;
     }
 }
 
 // ---------------------------------------------------------------------------------
-AI_FORCE_INLINE std::string GetNextToken(const char *&in) {
-    SkipSpacesAndLineEnd(&in);
+AI_FORCE_INLINE std::string GetNextToken(const char *&in, const char *end) {
+    SkipSpacesAndLineEnd(&in, end);
     const char *cur = in;
     while (!IsSpaceOrNewLine(*in)) {
         ++in;