From be787f5c6c441104665709e88df66c80c3689b45 Mon Sep 17 00:00:00 2001 From: Jesper Smith Date: Thu, 27 Jul 2017 17:42:01 -0500 Subject: [PATCH 001/490] Added supported for custom IO Systems in Java. Implemented ClassLoader IO System --- port/jassimp/jassimp-native/src/jassimp.cpp | 258 +++++++++++++++++- port/jassimp/jassimp-native/src/jassimp.h | 2 +- .../src/jassimp/AiClassLoaderIOSystem.java | 130 +++++++++ .../jassimp/src/jassimp/AiIOStream.java | 55 ++++ .../jassimp/src/jassimp/AiIOSystem.java | 54 ++++ .../src/jassimp/AiInputStreamIOStream.java | 102 +++++++ port/jassimp/jassimp/src/jassimp/Jassimp.java | 36 ++- 7 files changed, 619 insertions(+), 18 deletions(-) create mode 100644 port/jassimp/jassimp/src/jassimp/AiClassLoaderIOSystem.java create mode 100644 port/jassimp/jassimp/src/jassimp/AiIOStream.java create mode 100644 port/jassimp/jassimp/src/jassimp/AiIOSystem.java create mode 100644 port/jassimp/jassimp/src/jassimp/AiInputStreamIOStream.java diff --git a/port/jassimp/jassimp-native/src/jassimp.cpp b/port/jassimp/jassimp-native/src/jassimp.cpp index 226e416aa..475f6c5a0 100644 --- a/port/jassimp/jassimp-native/src/jassimp.cpp +++ b/port/jassimp/jassimp-native/src/jassimp.cpp @@ -1,7 +1,9 @@ #include "jassimp.h" -#include +#include #include +#include +#include #ifdef JNI_LOG @@ -12,9 +14,11 @@ #define lprintf(...) printf (__VA_ARGS__) #endif /* ANDROID */ #else -#define lprintf +#define lprintf #endif +static std::string gLastErrorString; + // Automatically deletes a local ref when it goes out of scope class SmartLocalRef { private: @@ -270,6 +274,81 @@ static bool callv(JNIEnv *env, jobject object, const char* typeName, return true; } +static jobject callo(JNIEnv *env, jobject object, const char* typeName, const char* methodName, + const char* signature,/* const*/ jvalue* params) +{ + jclass clazz = env->FindClass(typeName); + SmartLocalRef clazzRef(env, clazz); + + if (NULL == clazz) + { + lprintf("could not find class %s\n", typeName); + return NULL; + } + + jmethodID mid = env->GetMethodID(clazz, methodName, signature); + + if (NULL == mid) + { + lprintf("could not find method %s with signature %s in type %s\n", methodName, signature, typeName); + return NULL; + } + + jobject jReturnValue = env->CallObjectMethodA(object, mid, params); + + return jReturnValue; +} + +static int calli(JNIEnv *env, jobject object, const char* typeName, const char* methodName, + const char* signature) +{ + jclass clazz = env->FindClass(typeName); + SmartLocalRef clazzRef(env, clazz); + + if (NULL == clazz) + { + lprintf("could not find class %s\n", typeName); + return false; + } + + jmethodID mid = env->GetMethodID(clazz, methodName, signature); + + if (NULL == mid) + { + lprintf("could not find method %s with signature %s in type %s\n", methodName, signature, typeName); + return false; + } + + jint jReturnValue = env->CallIntMethod(object, mid); + + return (int) jReturnValue; +} + +static int callc(JNIEnv *env, jobject object, const char* typeName, const char* methodName, + const char* signature) +{ + jclass clazz = env->FindClass(typeName); + SmartLocalRef clazzRef(env, clazz); + + if (NULL == clazz) + { + lprintf("could not find class %s\n", typeName); + return false; + } + + jmethodID mid = env->GetMethodID(clazz, methodName, signature); + + if (NULL == mid) + { + lprintf("could not find method %s with signature %s in type %s\n", methodName, signature, typeName); + return false; + } + + jint jReturnValue = env->CallCharMethod(object, mid); + + return (int) jReturnValue; +} + static bool callStaticObject(JNIEnv *env, const char* typeName, const char* methodName, const char* signature,/* const*/ jvalue* params, jobject& returnValue) @@ -359,6 +438,155 @@ static bool copyBufferArray(JNIEnv *env, jobject jMesh, const char* jBufferName, return true; } +class JavaIOStream : public Assimp::IOStream +{ +private: + size_t pos; + size_t size; + char* buffer; + jobject jIOStream; + + +public: + JavaIOStream(size_t size, char* buffer, jobject jIOStream) : + pos(0), + size(size), + buffer(buffer), + jIOStream(jIOStream) + {}; + + + ~JavaIOStream(void) + { + free(buffer); + }; + + size_t Read(void* pvBuffer, size_t pSize, size_t pCount) + { + const size_t cnt = std::min(pCount,(size - pos)/pSize); + const size_t ofs = pSize*cnt; + + memcpy(pvBuffer, buffer + pos, ofs); + pos += ofs; + + return cnt; + }; + size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) {}; + + aiReturn Seek(size_t pOffset, aiOrigin pOrigin) + { + if (aiOrigin_SET == pOrigin) { + if (pOffset >= size) { + return AI_FAILURE; + } + pos = pOffset; + } + else if (aiOrigin_END == pOrigin) { + if (pOffset >= size) { + return AI_FAILURE; + } + pos = size-pOffset; + } + else { + if (pOffset + pos >= size) { + return AI_FAILURE; + } + pos += pOffset; + } + return AI_SUCCESS; + }; + + size_t Tell(void) const + { + return pos; + }; + + size_t FileSize() const + { + return size; + }; + + void Flush() {}; + + + jobject javaObject() + { + return jIOStream; + }; + + +}; + + +class JavaIOSystem : public Assimp::IOSystem { + private: + JNIEnv* mJniEnv; + jobject& mJavaIOSystem; + + public: + JavaIOSystem(JNIEnv* env, jobject& javaIOSystem) : + mJniEnv(env), + mJavaIOSystem(javaIOSystem) + {}; + + bool Exists( const char* pFile) const + { + jvalue params[1]; + params[0].l = mJniEnv->NewStringUTF(pFile); + return call(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "exists", "(Ljava/lang/String;)Z", params); + + }; + char getOsSeparator() const + { + return (char) callc(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "getOsSeparator", "()C"); + }; + + Assimp::IOStream* Open(const char* pFile,const char* pMode = "rb") + { + jvalue params[2]; + params[0].l = mJniEnv->NewStringUTF(pFile); + params[1].l = mJniEnv->NewStringUTF(pMode); + + + jobject jStream = callo(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "open", "(Ljava/lang/String;Ljava/lang/String;)Ljassimp/AiIOStream;", params); + if(NULL == jStream) + { + lprintf("NULL object from AiIOSystem.open\n"); + return NULL; + } + + size_t size = calli(mJniEnv, jStream, "jassimp/AiIOStream", "getFileSize", "()I"); + lprintf("Model file size is %d\n", size); + + char* buffer = (char*)malloc(size); + jobject javaBuffer = mJniEnv->NewDirectByteBuffer(buffer, size); + + jvalue readParams[1]; + readParams[0].l = javaBuffer; + if(call(mJniEnv, jStream, "jassimp/AiIOStream", "read", "(Ljava/nio/ByteBuffer;)Z", readParams)) + { + return new JavaIOStream(size, buffer, jStream); + } + else + { + lprintf("Read failure on AiIOStream.read"); + free(buffer); + return NULL; + } + + }; + void Close( Assimp::IOStream* pFile) + { + + jvalue params[1]; + params[0].l = ((JavaIOStream*) pFile)->javaObject(); + callv(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "close", "(Ljassimp/AiIOStream;)V", params); + delete pFile; + }; + + + +}; static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) @@ -1474,7 +1702,7 @@ JNIEXPORT jint JNICALL Java_jassimp_Jassimp_getlongsize JNIEXPORT jstring JNICALL Java_jassimp_Jassimp_getErrorString (JNIEnv *env, jclass jClazz) { - const char *err = aiGetErrorString(); + const char *err = gLastErrorString.c_str(); if (NULL == err) { @@ -1486,18 +1714,26 @@ JNIEXPORT jstring JNICALL Java_jassimp_Jassimp_getErrorString JNIEXPORT jobject JNICALL Java_jassimp_Jassimp_aiImportFile - (JNIEnv *env, jclass jClazz, jstring jFilename, jlong postProcess) + (JNIEnv *env, jclass jClazz, jstring jFilename, jlong postProcess, jobject ioSystem) { jobject jScene = NULL; /* convert params */ const char* cFilename = env->GetStringUTFChars(jFilename, NULL); + + Assimp::Importer imp; - + + if(ioSystem != NULL) + { + imp.SetIOHandler(new JavaIOSystem(env, ioSystem)); + lprintf("Created aiFileIO\n"); + } + lprintf("opening file: %s\n", cFilename); /* do import */ - const aiScene *cScene = aiImportFile(cFilename, (unsigned int) postProcess); + const aiScene *cScene = imp.ReadFile(cFilename, (unsigned int) postProcess); if (!cScene) { @@ -1552,19 +1788,13 @@ error: /* thats really a problem because we cannot throw in this case */ env->FatalError("could not throw java.io.IOException"); } - - env->ThrowNew(exception, aiGetErrorString()); + gLastErrorString = imp.GetErrorString(); + env->ThrowNew(exception, gLastErrorString.c_str()); lprintf("problem detected\n"); } end: - /* - * NOTE: this releases all memory used in the native domain. - * Ensure all data has been passed to java before! - */ - aiReleaseImport(cScene); - /* free params */ env->ReleaseStringUTFChars(jFilename, cFilename); diff --git a/port/jassimp/jassimp-native/src/jassimp.h b/port/jassimp/jassimp-native/src/jassimp.h index f448dc2c2..7d4b66e29 100644 --- a/port/jassimp/jassimp-native/src/jassimp.h +++ b/port/jassimp/jassimp-native/src/jassimp.h @@ -39,7 +39,7 @@ JNIEXPORT jstring JNICALL Java_jassimp_Jassimp_getErrorString * Signature: (Ljava/lang/String;J)Ljassimp/AiScene; */ JNIEXPORT jobject JNICALL Java_jassimp_Jassimp_aiImportFile - (JNIEnv *, jclass, jstring, jlong); + (JNIEnv *, jclass, jstring, jlong, jobject); #ifdef __cplusplus } diff --git a/port/jassimp/jassimp/src/jassimp/AiClassLoaderIOSystem.java b/port/jassimp/jassimp/src/jassimp/AiClassLoaderIOSystem.java new file mode 100644 index 000000000..04d638610 --- /dev/null +++ b/port/jassimp/jassimp/src/jassimp/AiClassLoaderIOSystem.java @@ -0,0 +1,130 @@ +/* + * Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jassimp; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +/** + * IOSystem based on the Java classloader.

+ * + * This IOSystem allows loading models directly from the + * classpath. No extraction to the file system is + * necessary. + * + * @author Jesper Smith + * + */ +public class AiClassLoaderIOSystem implements AiIOSystem +{ + private final Class clazz; + private final ClassLoader classLoader; + + /** + * Construct a new AiClassLoaderIOSystem.

+ * + * This constructor uses a ClassLoader to resolve + * resources. + * + * @param classLoader classLoader to resolve resources. + */ + public AiClassLoaderIOSystem(ClassLoader classLoader) { + this.clazz = null; + this.classLoader = classLoader; + } + + /** + * Construct a new AiClassLoaderIOSystem.

+ * + * This constructor uses a Class to resolve + * resources. + * + * @param class class to resolve resources. + */ + public AiClassLoaderIOSystem(Class clazz) { + this.clazz = clazz; + this.classLoader = null; + } + + + @Override + public AiInputStreamIOStream open(String filename, String ioMode) { + try { + + InputStream is; + + if(clazz != null) { + is = clazz.getResourceAsStream(filename); + } + else if (classLoader != null) { + is = classLoader.getResourceAsStream(filename); + } + else { + System.err.println("[" + getClass().getSimpleName() + + "] No class or classLoader provided to resolve " + filename); + return null; + } + + if(is != null) { + return new AiInputStreamIOStream(is); + } + else { + System.err.println("[" + getClass().getSimpleName() + + "] Cannot find " + filename); + return null; + } + } + catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + @Override + public void close(AiInputStreamIOStream file) { + } + + @Override + public boolean exists(String path) + { + URL url = null; + if(clazz != null) { + url = clazz.getResource(path); + } + else if (classLoader != null) { + url = classLoader.getResource(path); + } + + + if(url == null) + { + return false; + } + else + { + return true; + } + + } + + @Override + public char getOsSeparator() + { + return '/'; + } + +} diff --git a/port/jassimp/jassimp/src/jassimp/AiIOStream.java b/port/jassimp/jassimp/src/jassimp/AiIOStream.java new file mode 100644 index 000000000..5378da5f8 --- /dev/null +++ b/port/jassimp/jassimp/src/jassimp/AiIOStream.java @@ -0,0 +1,55 @@ +/* + * Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jassimp; + +import java.nio.ByteBuffer; + + +/** + * Interface to allow custom resource loaders for jassimp.

+ * + * The design is based on passing the file wholly in memory, + * because Java inputstreams do not have to support seek.

+ * + * Writing files from Java is unsupported. + * + * + * @author Jesper Smith + * + */ +public interface AiIOStream +{ + + /** + * Read all data into buffer.

+ * + * The whole stream should be read into the buffer. + * No support is provided for partial reads. + * + * @param buffer Target buffer for the model data + * + * @return true if successful, false if an error occurred. + */ + boolean read(ByteBuffer buffer); + + /** + * The total size of this stream.

+ * + * @return total size of this stream + */ + int getFileSize(); + +} diff --git a/port/jassimp/jassimp/src/jassimp/AiIOSystem.java b/port/jassimp/jassimp/src/jassimp/AiIOSystem.java new file mode 100644 index 000000000..d2c741529 --- /dev/null +++ b/port/jassimp/jassimp/src/jassimp/AiIOSystem.java @@ -0,0 +1,54 @@ +/* + * Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jassimp; + +public interface AiIOSystem +{ + /** + * + * Open a new file with a given path. + * When the access to the file is finished, call close() to release all associated resources + * + * @param path Path to the file + * @param ioMode file I/O mode. Required are: "wb", "w", "wt", "rb", "r", "rt". + * + * @return AiIOStream or null if an error occurred + */ + public T open(String path, String ioMode); + + + /** + * Tests for the existence of a file at the given path. + * + * @param path path to the file + * @return true if there is a file with this path, else false. + */ + public boolean exists(String path); + + /** + * Returns the system specific directory separator.

+ * + * @return System specific directory separator + */ + public char getOsSeparator(); + + /** + * Closes the given file and releases all resources associated with it. + * + * @param file The file instance previously created by Open(). + */ + public void close(T file); +} diff --git a/port/jassimp/jassimp/src/jassimp/AiInputStreamIOStream.java b/port/jassimp/jassimp/src/jassimp/AiInputStreamIOStream.java new file mode 100644 index 000000000..998401b68 --- /dev/null +++ b/port/jassimp/jassimp/src/jassimp/AiInputStreamIOStream.java @@ -0,0 +1,102 @@ +/* + * Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jassimp; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.net.URL; +import java.nio.ByteBuffer; + + +/** + * Implementation of AiIOStream reading from a InputStream + * + * @author Jesper Smith + * + */ +public class AiInputStreamIOStream implements AiIOStream +{ + private final ByteArrayOutputStream os = new ByteArrayOutputStream(); + + + public AiInputStreamIOStream(URI uri) throws IOException { + this(uri.toURL()); + } + + public AiInputStreamIOStream(URL url) throws IOException { + this(url.openStream()); + } + + public AiInputStreamIOStream(InputStream is) throws IOException { + int read; + byte[] data = new byte[1024]; + while((read = is.read(data, 0, data.length)) != -1) { + os.write(data, 0, read); + } + os.flush(); + + is.close(); + } + + @Override + public int getFileSize() { + return os.size(); + } + + @Override + public boolean read(ByteBuffer buffer) { + ByteBufferOutputStream bos = new ByteBufferOutputStream(buffer); + try + { + os.writeTo(bos); + } + catch (IOException e) + { + e.printStackTrace(); + return false; + } + return true; + } + + /** + * Internal helper class to copy the contents of an OutputStream + * into a ByteBuffer. This avoids a copy. + * + */ + private static class ByteBufferOutputStream extends OutputStream { + + private final ByteBuffer buffer; + + public ByteBufferOutputStream(ByteBuffer buffer) { + this.buffer = buffer; + } + + @Override + public void write(int b) throws IOException + { + buffer.put((byte) b); + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + buffer.put(b, off, len); + } + } +} + diff --git a/port/jassimp/jassimp/src/jassimp/Jassimp.java b/port/jassimp/jassimp/src/jassimp/Jassimp.java index 92f4864c7..d1b4aae4e 100644 --- a/port/jassimp/jassimp/src/jassimp/Jassimp.java +++ b/port/jassimp/jassimp/src/jassimp/Jassimp.java @@ -79,6 +79,20 @@ public final class Jassimp { return importFile(filename, EnumSet.noneOf(AiPostProcessSteps.class)); } + /** + * Imports a file via assimp without post processing. + * + * @param filename the file to import + * @param ioSystem ioSystem to load files, or null for default + * @return the loaded scene + * @throws IOException if an error occurs + */ + public static AiScene importFile(String filename, AiIOSystem ioSystem) + throws IOException { + + return importFile(filename, EnumSet.noneOf(AiPostProcessSteps.class), ioSystem); + } + /** * Imports a file via assimp. @@ -89,12 +103,28 @@ public final class Jassimp { * @throws IOException if an error occurs */ public static AiScene importFile(String filename, - Set postProcessing) throws IOException { + Set postProcessing) + throws IOException { + return importFile(filename, postProcessing, null); + } + + /** + * Imports a file via assimp. + * + * @param filename the file to import + * @param postProcessing post processing flags + * @param ioSystem ioSystem to load files, or null for default + * @return the loaded scene, or null if an error occurred + * @throws IOException if an error occurs + */ + public static AiScene importFile(String filename, + Set postProcessing, AiIOSystem ioSystem) + throws IOException { loadLibrary(); return aiImportFile(filename, AiPostProcessSteps.toRawValue( - postProcessing)); + postProcessing), ioSystem); } @@ -310,7 +340,7 @@ public final class Jassimp { * @throws IOException if an error occurs */ private static native AiScene aiImportFile(String filename, - long postProcessing) throws IOException; + long postProcessing, AiIOSystem ioSystem) throws IOException; /** From ab9dda594dec379b3be09d9c7dc60fe7f53a8667 Mon Sep 17 00:00:00 2001 From: Jesper Smith Date: Fri, 28 Jul 2017 15:08:59 -0500 Subject: [PATCH 002/490] Added return statement to Write --- port/jassimp/jassimp-native/src/jassimp.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/port/jassimp/jassimp-native/src/jassimp.cpp b/port/jassimp/jassimp-native/src/jassimp.cpp index 475f6c5a0..75b1bc510 100644 --- a/port/jassimp/jassimp-native/src/jassimp.cpp +++ b/port/jassimp/jassimp-native/src/jassimp.cpp @@ -471,7 +471,10 @@ public: return cnt; }; - size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) {}; + size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) + { + return 0; + }; aiReturn Seek(size_t pOffset, aiOrigin pOrigin) { From c143d2e02cab75cdf5171b6f0dfc42569c061888 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 7 Sep 2017 20:30:17 +0200 Subject: [PATCH 003/490] closes https://github.com/assimp/assimp/issues/1404: set name with merged meshes for output mesh. --- code/3DSConverter.cpp | 9 +++-- code/SceneCombiner.cpp | 16 ++++++-- include/assimp/SceneCombiner.h | 7 +--- test/CMakeLists.txt | 1 + test/unit/utSceneCombiner.cpp | 70 ++++++++++++++++++++++++++++++++++ 5 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 test/unit/utSceneCombiner.cpp diff --git a/code/3DSConverter.cpp b/code/3DSConverter.cpp index da8c918a7..820c28f90 100644 --- a/code/3DSConverter.cpp +++ b/code/3DSConverter.cpp @@ -56,18 +56,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; +static const unsigned int NotSet = 0xcdcdcdcd; + // ------------------------------------------------------------------------------------------------ // Setup final material indices, generae a default material if necessary void Discreet3DSImporter::ReplaceDefaultMaterial() { - // Try to find an existing material that matches the // typical default material setting: // - no textures // - diffuse color (in grey!) // NOTE: This is here to workaround the fact that some // exporters are writing a default material, too. - unsigned int idx = 0xcdcdcdcd; + unsigned int idx( NotSet ); for (unsigned int i = 0; i < mScene->mMaterials.size();++i) { std::string s = mScene->mMaterials[i].mName; @@ -93,7 +94,9 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() } idx = i; } - if (0xcdcdcdcd == idx)idx = (unsigned int)mScene->mMaterials.size(); + if ( NotSet == idx ) { + idx = ( unsigned int )mScene->mMaterials.size(); + } // now iterate through all meshes and through all faces and // find all faces that are using the default material diff --git a/code/SceneCombiner.cpp b/code/SceneCombiner.cpp index 9bcce4275..6fb120325 100644 --- a/code/SceneCombiner.cpp +++ b/code/SceneCombiner.cpp @@ -58,6 +58,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "time.h" #include #include +#include #include #include "ScenePrivate.h" @@ -757,7 +758,7 @@ void SceneCombiner::MergeBones(aiMesh* out,std::vector::const_iterator // ------------------------------------------------------------------------------------------------ // Merge a list of meshes -void SceneCombiner::MergeMeshes(aiMesh** _out,unsigned int /*flags*/, +void SceneCombiner::MergeMeshes(aiMesh** _out, unsigned int /*flags*/, std::vector::const_iterator begin, std::vector::const_iterator end) { @@ -772,8 +773,14 @@ void SceneCombiner::MergeMeshes(aiMesh** _out,unsigned int /*flags*/, aiMesh* out = *_out = new aiMesh(); out->mMaterialIndex = (*begin)->mMaterialIndex; + std::string name; // Find out how much output storage we'll need - for (std::vector::const_iterator it = begin; it != end;++it) { + for (std::vector::const_iterator it = begin; it != end; ++it) { + const char *meshName( (*it)->mName.C_Str() ); + name += std::string( meshName ); + if ( it != end - 1 ) { + name += "."; + } out->mNumVertices += (*it)->mNumVertices; out->mNumFaces += (*it)->mNumFaces; out->mNumBones += (*it)->mNumBones; @@ -781,6 +788,7 @@ void SceneCombiner::MergeMeshes(aiMesh** _out,unsigned int /*flags*/, // combine primitive type flags out->mPrimitiveTypes |= (*it)->mPrimitiveTypes; } + out->mName.Set( name.c_str() ); if (out->mNumVertices) { aiVector3D* pv2; @@ -789,7 +797,7 @@ void SceneCombiner::MergeMeshes(aiMesh** _out,unsigned int /*flags*/, if ((**begin).HasPositions()) { pv2 = out->mVertices = new aiVector3D[out->mNumVertices]; - for (std::vector::const_iterator it = begin; it != end;++it) { + for (std::vector::const_iterator it = begin; it != end; ++it) { if ((*it)->mVertices) { ::memcpy(pv2,(*it)->mVertices,(*it)->mNumVertices*sizeof(aiVector3D)); } @@ -809,7 +817,7 @@ void SceneCombiner::MergeMeshes(aiMesh** _out,unsigned int /*flags*/, pv2 += (*it)->mNumVertices; } } - // copy tangents and bitangents + // copy tangents and bi-tangents if ((**begin).HasTangentsAndBitangents()) { pv2 = out->mTangents = new aiVector3D[out->mNumVertices]; diff --git a/include/assimp/SceneCombiner.h b/include/assimp/SceneCombiner.h index ca4e68c7f..ebb5dda00 100644 --- a/include/assimp/SceneCombiner.h +++ b/include/assimp/SceneCombiner.h @@ -217,10 +217,9 @@ public: static void MergeScenes(aiScene** dest,std::vector& src, unsigned int flags = 0); - // ------------------------------------------------------------------- - /** Merges two or more scenes and attaches all sceenes to a specific - * position in the node graph of the masteer scene. + /** Merges two or more scenes and attaches all scenes to a specific + * position in the node graph of the master scene. * * @param dest Receives a pointer to the destination scene. If the * pointer doesn't point to NULL when the function is called, the @@ -236,7 +235,6 @@ public: std::vector& src, unsigned int flags = 0); - // ------------------------------------------------------------------- /** Merges two or more meshes * @@ -255,7 +253,6 @@ public: std::vector::const_iterator begin, std::vector::const_iterator end); - // ------------------------------------------------------------------- /** Merges two or more bones * diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8627efe14..cb559deb3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -116,6 +116,7 @@ SET( TEST_SRCS unit/utRemoveRedundantMaterials.cpp unit/utRemoveVCProcess.cpp unit/utScenePreprocessor.cpp + unit/utSceneCombiner.cpp unit/utSharedPPData.cpp unit/utStringUtils.cpp unit/utSMDImportExport.cpp diff --git a/test/unit/utSceneCombiner.cpp b/test/unit/utSceneCombiner.cpp new file mode 100644 index 000000000..f0e4d5d90 --- /dev/null +++ b/test/unit/utSceneCombiner.cpp @@ -0,0 +1,70 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +#include "UnitTestPCH.h" +#include +#include + +using namespace ::Assimp; + +class utSceneCombiner : public ::testing::Test { + // empty +}; + +TEST_F( utSceneCombiner, MergeMeshes_ValidNames_Test ) { + std::vector merge_list; + aiMesh *mesh1 = new aiMesh; + mesh1->mName.Set( "mesh_1" ); + merge_list.push_back( mesh1 ); + + aiMesh *mesh2 = new aiMesh; + mesh2->mName.Set( "mesh_2" ); + merge_list.push_back( mesh2 ); + + aiMesh *mesh3 = new aiMesh; + mesh3->mName.Set( "mesh_3" ); + merge_list.push_back( mesh3 ); + + aiMesh *out( nullptr ); + SceneCombiner::MergeMeshes( &out, 0, merge_list.begin(), merge_list.end() ); + std::string outName = out->mName.C_Str(); + EXPECT_EQ( "mesh_1.mesh_2.mesh_3", outName ); +} From e4c15575614e1bd4715443cb502a3afc49cbeef1 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 7 Sep 2017 22:02:09 +0200 Subject: [PATCH 004/490] Traivis: make build amtrix smaller. --- .travis.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index ffebab3cb..38849dae4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,14 +28,16 @@ env: - secure: "lZ7pHQvl5dpZWzBQAaIMf0wqrvtcZ4wiZKeIZjf83TEsflW8+z0uTpIuN30ZV6Glth/Sq1OhLnTP5+N57fZU/1ebA5twHdvP4bS5CIUUg71/CXQZNl36xeaqvxsG/xRrdpKOsPdjAOsQ9KPTQulsX43XDLS7CasMiLvYOpqKcPc=" - PV=r8e PLATF=linux-x86_64 NDK_HOME=${TRAVIS_BUILD_DIR}/android-ndk-${PV} PATH=${PATH}:${NDK_HOME} matrix: - - LINUX=1 TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON - - LINUX=1 TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF - - LINUX=1 SHARED_BUILD=ON ENABLE_COVERALLS=OFF - - LINUX=1 SHARED_BUILD=OFF ENABLE_COVERALLS=OFF - -compiler: - - gcc - - clang + - os: linux LINUX=1 TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON + compiler: gcc + - os: linux LINUX=1 TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF + compiler: clang + - os: linux LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF + compiler: gcc + - os: linux LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF + compiler: clang + - os: osx + osx_image: xcode8.2 install: - if [ $ANDROID ]; then wget -c http://dl.google.com/android/ndk/android-ndk-${PV}-${PLATF}.tar.bz2 && tar xf android-ndk-${PV}-${PLATF}.tar.bz2 ; fi From c2baa0f59d06cc670c624c388761dd32ec1b8bee Mon Sep 17 00:00:00 2001 From: Giuseppe Barbieri Date: Fri, 8 Sep 2017 15:46:23 +0200 Subject: [PATCH 005/490] Added a link to pure jvm assimp port --- Readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Readme.md b/Readme.md index 88dc6fbc3..a5c31881a 100644 --- a/Readme.md +++ b/Readme.md @@ -119,6 +119,7 @@ Take a look into the `INSTALL` file. Our build system is CMake, if you used CMak * [Pascal](port/AssimpPascal/Readme.md) * [Javascript (Alpha)](https://github.com/makc/assimp2json) * [Unity 3d Plugin] (https://www.assetstore.unity3d.com/en/#!/content/91777) +* [JVM](https://github.com/kotlin-graphics/assimp) Full jvm port (currently supported obj, ply, stl, ~collada) ### Other tools ### [open3mod](https://github.com/acgessler/open3mod) is a powerful 3D model viewer based on Assimp's import and export abilities. From 2ec46cc1882f5f2ebb4402eee0ff181a7132d169 Mon Sep 17 00:00:00 2001 From: Amit Cirt Date: Sat, 9 Sep 2017 19:04:15 +0300 Subject: [PATCH 006/490] fix name lost in mesh and nodes when load with aiProcess_PreTransformVertices flag --- code/PretransformVertices.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/code/PretransformVertices.cpp b/code/PretransformVertices.cpp index 1339053f7..7bfed4292 100644 --- a/code/PretransformVertices.cpp +++ b/code/PretransformVertices.cpp @@ -160,6 +160,11 @@ void PretransformVertices::CollectData( aiScene* pcScene, aiNode* pcNode, unsign unsigned int& num_ref = num_refs[pcNode->mMeshes[i]]; ai_assert(0 != num_ref); --num_ref; + // Save the name of the last mesh + if (num_ref==0) + { + pcMeshOut->mName = pcMesh->mName; + } if (identity) { // copy positions without modifying them @@ -626,9 +631,10 @@ void PretransformVertices::Execute( aiScene* pScene) // now delete all nodes in the scene and build a new // flat node graph with a root node and some level 1 children + aiNode* newRoot = new aiNode(); + newRoot->mName = pScene->mRootNode->mName; delete pScene->mRootNode; pScene->mRootNode = new aiNode(); - pScene->mRootNode->mName.Set(""); if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras) { @@ -646,7 +652,7 @@ void PretransformVertices::Execute( aiScene* pScene) { aiNode* pcNode = *nodes = new aiNode(); pcNode->mParent = pScene->mRootNode; - pcNode->mName.length = ::ai_snprintf(pcNode->mName.data,MAXLEN,"mesh_%u",i); + pcNode->mName = pScene->mMeshes[i]->mName; // setup mesh indices pcNode->mNumMeshes = 1; From 97b67d5cb5c5ca0ccb9ccf62390dee3dbec1d3ee Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 10 Sep 2017 18:47:24 +1000 Subject: [PATCH 007/490] Fixed warnings when compiling for x64 on MSVC through VS 2017 v15.3.3. --- code/AssbinExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssbinExporter.cpp b/code/AssbinExporter.cpp index e97f27a8a..4ea261718 100644 --- a/code/AssbinExporter.cpp +++ b/code/AssbinExporter.cpp @@ -325,7 +325,7 @@ inline size_t WriteArray(IOStream * stream, const T* in, unsigned int size) { AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AINODE ); - size_t nb_metadata = (node->mMetaData != NULL ? node->mMetaData->mNumProperties : 0); + unsigned int nb_metadata = (node->mMetaData != NULL ? node->mMetaData->mNumProperties : 0); Write(&chunk,node->mName); Write(&chunk,node->mTransformation); From 98532b45bfab304e579c7aef0b16a6cd00142500 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 10 Sep 2017 19:04:44 +1000 Subject: [PATCH 008/490] Fixed warnings when compiling for x64 on MSVC through VS 2017 v15.3.3. --- code/ColladaExporter.cpp | 10 +++++----- code/ColladaLoader.cpp | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/code/ColladaExporter.cpp b/code/ColladaExporter.cpp index 745975e5d..06604bc8e 100644 --- a/code/ColladaExporter.cpp +++ b/code/ColladaExporter.cpp @@ -866,8 +866,8 @@ void ColladaExporter::WriteController( size_t pIndex) std::vector bind_poses; bind_poses.reserve(mesh->mNumBones * 16); - for( size_t i = 0; i < mesh->mNumBones; ++i) - for( size_t j = 0; j < 4; ++j) + for(unsigned int i = 0; i < mesh->mNumBones; ++i) + for( unsigned int j = 0; j < 4; ++j) bind_poses.insert(bind_poses.end(), mesh->mBones[i]->mOffsetMatrix[j], mesh->mBones[i]->mOffsetMatrix[j] + 4); WriteFloatArray( idstr + "-skin-bind_poses", FloatType_Mat4x4, (const ai_real*) bind_poses.data(), bind_poses.size() / 16); @@ -924,11 +924,11 @@ void ColladaExporter::WriteController( size_t pIndex) ai_uint weight_index = 0; std::vector joint_weight_indices(2 * joint_weight_indices_length, (ai_int)-1); - for( size_t i = 0; i < mesh->mNumBones; ++i) - for( size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j) + for( unsigned int i = 0; i < mesh->mNumBones; ++i) + for( unsigned j = 0; j < mesh->mBones[i]->mNumWeights; ++j) { unsigned int vId = mesh->mBones[i]->mWeights[j].mVertexId; - for( size_t k = 0; k < num_influences[vId]; ++k) + for( ai_uint k = 0; k < num_influences[vId]; ++k) { if (joint_weight_indices[2 * (accum_influences[vId] + k)] == -1) { diff --git a/code/ColladaLoader.cpp b/code/ColladaLoader.cpp index c68eb27c9..f7ef903f4 100644 --- a/code/ColladaLoader.cpp +++ b/code/ColladaLoader.cpp @@ -728,7 +728,7 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada:: ? aiMorphingMethod_MORPH_RELATIVE : aiMorphingMethod_MORPH_NORMALIZED; dstMesh->mAnimMeshes = new aiAnimMesh*[animMeshes.size()]; - dstMesh->mNumAnimMeshes = animMeshes.size(); + dstMesh->mNumAnimMeshes = static_cast(animMeshes.size()); for (unsigned int i = 0; i < animMeshes.size(); i++) dstMesh->mAnimMeshes[i] = animMeshes.at(i); } @@ -1377,9 +1377,9 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars { aiNodeAnim* dstAnim = new aiNodeAnim; dstAnim->mNodeName = nodeName; - dstAnim->mNumPositionKeys = resultTrafos.size(); - dstAnim->mNumRotationKeys= resultTrafos.size(); - dstAnim->mNumScalingKeys = resultTrafos.size(); + dstAnim->mNumPositionKeys = static_cast(resultTrafos.size()); + dstAnim->mNumRotationKeys = static_cast(resultTrafos.size()); + dstAnim->mNumScalingKeys = static_cast(resultTrafos.size()); dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()]; dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()]; dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()]; @@ -1445,11 +1445,11 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars ++morphAnimChannelIndex; } - morphAnim->mNumKeys = morphTimeValues.size(); + morphAnim->mNumKeys = static_cast(morphTimeValues.size()); morphAnim->mKeys = new aiMeshMorphKey[morphAnim->mNumKeys]; for (unsigned int key = 0; key < morphAnim->mNumKeys; key++) { - morphAnim->mKeys[key].mNumValuesAndWeights = morphChannels.size(); + morphAnim->mKeys[key].mNumValuesAndWeights = static_cast(morphChannels.size()); morphAnim->mKeys[key].mValues = new unsigned int [morphChannels.size()]; morphAnim->mKeys[key].mWeights = new double [morphChannels.size()]; @@ -1470,13 +1470,13 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars { aiAnimation* anim = new aiAnimation; anim->mName.Set( pName); - anim->mNumChannels = anims.size(); + anim->mNumChannels = static_cast(anims.size()); if (anim->mNumChannels > 0) { anim->mChannels = new aiNodeAnim*[anims.size()]; std::copy( anims.begin(), anims.end(), anim->mChannels); } - anim->mNumMorphMeshChannels = morphAnims.size(); + anim->mNumMorphMeshChannels = static_cast(morphAnims.size()); if (anim->mNumMorphMeshChannels > 0) { anim->mMorphMeshChannels = new aiMeshMorphAnim*[anim->mNumMorphMeshChannels]; From b7f1277175236b6ce935b9ca9cc2b210b47905b3 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 10 Sep 2017 19:07:37 +1000 Subject: [PATCH 009/490] Fixed warnings when compiling for x64 on MSVC through VS 2017 v15.3.3. --- code/ObjFileMtlImporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ObjFileMtlImporter.cpp b/code/ObjFileMtlImporter.cpp index 69f35a67d..36bb6c2cb 100644 --- a/code/ObjFileMtlImporter.cpp +++ b/code/ObjFileMtlImporter.cpp @@ -304,7 +304,7 @@ void ObjFileMtlImporter::createMaterial() m_pModel->m_pCurrentMaterial = new ObjFile::Material(); m_pModel->m_pCurrentMaterial->MaterialName.Set( name ); if (m_pModel->m_pCurrentMesh) { - m_pModel->m_pCurrentMesh->m_uiMaterialIndex = m_pModel->m_MaterialLib.size() - 1; + m_pModel->m_pCurrentMesh->m_uiMaterialIndex = static_cast(m_pModel->m_MaterialLib.size() - 1); } m_pModel->m_MaterialLib.push_back( name ); m_pModel->m_MaterialMap[ name ] = m_pModel->m_pCurrentMaterial; From 6e02bcd8d6b187d10f30c536dbd2e3bf3c3af85d Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 10 Sep 2017 19:10:59 +1000 Subject: [PATCH 010/490] Fixed warnings when compiling for x64 on MSVC through VS 2017 v15.3.3. --- code/PlyParser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/PlyParser.cpp b/code/PlyParser.cpp index d978b513b..f4b68a83f 100644 --- a/code/PlyParser.cpp +++ b/code/PlyParser.cpp @@ -618,7 +618,7 @@ bool PLY::DOM::ParseInstanceBinary(IOStreamBuffer &streamBuffer, DOM* p_pc } streamBuffer.getNextBlock(buffer); - unsigned int bufferSize = buffer.size(); + unsigned int bufferSize = static_cast(buffer.size()); const char* pCur = (char*)&buffer[0]; if (!p_pcOut->ParseElementInstanceListsBinary(streamBuffer, buffer, pCur, bufferSize, loader, p_bBE)) { @@ -1025,7 +1025,7 @@ bool PLY::PropertyInstance::ParseValueBinary(IOStreamBuffer &streamBuffer, buffer = std::vector(buffer.end() - bufferSize, buffer.end()); buffer.insert(buffer.end(), nbuffer.begin(), nbuffer.end()); nbuffer.clear(); - bufferSize = buffer.size(); + bufferSize = static_cast(buffer.size()); pCur = (char*)&buffer[0]; } else From 6db0a63d6ef5e2b70014d342810955abc6cec088 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 10 Sep 2017 19:13:12 +1000 Subject: [PATCH 011/490] Fixed warnings when compiling for x64 on MSVC through VS 2017 v15.3.3. --- code/IFCProfile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/IFCProfile.cpp b/code/IFCProfile.cpp index e94d98a04..595159a6b 100644 --- a/code/IFCProfile.cpp +++ b/code/IFCProfile.cpp @@ -128,7 +128,7 @@ void ProcessParametrizedProfile(const IfcParameterizedProfileDef& def, TempMesh& meshout.verts.push_back( IfcVector3( std::cos(angle)*radius, std::sin(angle)*radius, 0.f )); } - meshout.vertcnt.push_back(segments); + meshout.vertcnt.push_back(static_cast(segments)); } else if( const IfcIShapeProfileDef* const ishape = def.ToPtr()) { // construct simplified IBeam shape From 1167edaeca09b07a8ea20036d83d626180d48175 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Mon, 11 Sep 2017 00:09:35 +1000 Subject: [PATCH 012/490] Fixed warnings when compiling for x64 on MSVC through VS 2017 v15.3.3. --- code/MMDImporter.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/MMDImporter.cpp b/code/MMDImporter.cpp index 149761c34..84797cba2 100644 --- a/code/MMDImporter.cpp +++ b/code/MMDImporter.cpp @@ -278,7 +278,7 @@ aiMesh *MMDImporter::CreateMesh(const pmx::PmxModel *pModel, bone_vertex_map[vsBDEF2_ptr->bone_index1].push_back( aiVertexWeight(index, vsBDEF2_ptr->bone_weight)); bone_vertex_map[vsBDEF2_ptr->bone_index2].push_back( - aiVertexWeight(index, 1.0 - vsBDEF2_ptr->bone_weight)); + aiVertexWeight(index, 1.0f - vsBDEF2_ptr->bone_weight)); break; case pmx::PmxVertexSkinningType::BDEF4: bone_vertex_map[vsBDEF4_ptr->bone_index1].push_back( @@ -295,7 +295,7 @@ aiMesh *MMDImporter::CreateMesh(const pmx::PmxModel *pModel, bone_vertex_map[vsSDEF_ptr->bone_index1].push_back( aiVertexWeight(index, vsSDEF_ptr->bone_weight)); bone_vertex_map[vsSDEF_ptr->bone_index2].push_back( - aiVertexWeight(index, 1.0 - vsSDEF_ptr->bone_weight)); + aiVertexWeight(index, 1.0f - vsSDEF_ptr->bone_weight)); break; case pmx::PmxVertexSkinningType::QDEF: const auto vsQDEF_ptr = @@ -325,7 +325,7 @@ aiMesh *MMDImporter::CreateMesh(const pmx::PmxModel *pModel, aiMatrix4x4::Translation(-pos, pBone->mOffsetMatrix); auto it = bone_vertex_map.find(ii); if (it != bone_vertex_map.end()) { - pBone->mNumWeights = it->second.size(); + pBone->mNumWeights = static_cast(it->second.size()); pBone->mWeights = it->second.data(); it->second.swap(*(new vector)); } From e40cd6c13c0d5c85e932c4d958c4c13d8b6d1d20 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Mon, 11 Sep 2017 00:09:40 +1000 Subject: [PATCH 013/490] Fixed warnings when compiling for x64 on MSVC through VS 2017 v15.3.3. --- code/X3DImporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/X3DImporter.cpp b/code/X3DImporter.cpp index 669f46f95..aaf99b309 100644 --- a/code/X3DImporter.cpp +++ b/code/X3DImporter.cpp @@ -558,7 +558,7 @@ void X3DImporter::XML_ReadNode_GetAttrVal_AsArrF(const int pAttrIdx, std::vector WordIterator wordItBegin(val, val + strlen(val)); WordIterator wordItEnd; - std::transform(wordItBegin, wordItEnd, std::back_inserter(pValue), [](const char *match) { return atof(match); }); + std::transform(wordItBegin, wordItEnd, std::back_inserter(pValue), [](const char *match) { return static_cast(atof(match)); }); } } From 698cd5826d5be44964a24fb32a1c3c92f0d8daac Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Mon, 11 Sep 2017 00:16:26 +1000 Subject: [PATCH 014/490] Fixed warnings when compiling for x64 on MSVC through VS 2017 v15.3.3. --- code/glTF2Asset.inl | 2 +- code/glTF2Exporter.cpp | 10 +++++----- code/glTFExporter.cpp | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 44a1cce00..09e647cca 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -242,7 +242,7 @@ Ref LazyDict::Create(const char* id) } T* inst = new T(); inst->id = id; - inst->index = mObjs.size(); + inst->index = static_cast(mObjs.size()); return Add(inst); } diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 11694a89d..7daef7000 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -886,7 +886,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, RefmPositionKeys[frameIndex].mTime / ticksPerSecond; } - Ref timeAccessor = ExportData(mAsset, animId, buffer, numKeyframes, &timeData[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_FLOAT); + Ref timeAccessor = ExportData(mAsset, animId, buffer, static_cast(numKeyframes), &timeData[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_FLOAT); if (timeAccessor) animRef->Parameters.TIME = timeAccessor; } @@ -899,7 +899,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, RefmPositionKeys[frameIndex].mValue; } - Ref tranAccessor = ExportData(mAsset, animId, buffer, numKeyframes, translationData, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT); + Ref tranAccessor = ExportData(mAsset, animId, buffer, static_cast(numKeyframes), translationData, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT); if ( tranAccessor ) { animRef->Parameters.translation = tranAccessor; } @@ -915,7 +915,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, RefmScalingKeys[frameIndex].mValue; } - Ref scaleAccessor = ExportData(mAsset, animId, buffer, numKeyframes, scaleData, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT); + Ref scaleAccessor = ExportData(mAsset, animId, buffer, static_cast(numKeyframes), scaleData, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT); if ( scaleAccessor ) { animRef->Parameters.scale = scaleAccessor; } @@ -934,7 +934,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, RefmRotationKeys[frameIndex].mValue.w; } - Ref rotAccessor = ExportData(mAsset, animId, buffer, numKeyframes, rotationData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT); + Ref rotAccessor = ExportData(mAsset, animId, buffer, static_cast(numKeyframes), rotationData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT); if ( rotAccessor ) { animRef->Parameters.rotation = rotAccessor; } @@ -989,7 +989,7 @@ void glTF2Exporter::ExportAnimations() Animation::AnimChannel tmpAnimChannel; Animation::AnimSampler tmpAnimSampler; - tmpAnimChannel.sampler = animRef->Samplers.size(); + tmpAnimChannel.sampler = static_cast(animRef->Samplers.size()); tmpAnimChannel.target.path = channelType; tmpAnimSampler.output = channelType; tmpAnimSampler.id = name + "_" + channelType; diff --git a/code/glTFExporter.cpp b/code/glTFExporter.cpp index f3fa5e526..8967a7aa7 100644 --- a/code/glTFExporter.cpp +++ b/code/glTFExporter.cpp @@ -875,7 +875,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, RefmPositionKeys[frameIndex].mTime / ticksPerSecond; } - Ref timeAccessor = ExportData(mAsset, animId, buffer, numKeyframes, &timeData[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_FLOAT); + Ref timeAccessor = ExportData(mAsset, animId, buffer, static_cast(numKeyframes), &timeData[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_FLOAT); if (timeAccessor) animRef->Parameters.TIME = timeAccessor; } @@ -888,7 +888,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, RefmPositionKeys[frameIndex].mValue; } - Ref tranAccessor = ExportData(mAsset, animId, buffer, numKeyframes, translationData, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT); + Ref tranAccessor = ExportData(mAsset, animId, buffer, static_cast(numKeyframes), translationData, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT); if ( tranAccessor ) { animRef->Parameters.translation = tranAccessor; } @@ -904,7 +904,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, RefmScalingKeys[frameIndex].mValue; } - Ref scaleAccessor = ExportData(mAsset, animId, buffer, numKeyframes, scaleData, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT); + Ref scaleAccessor = ExportData(mAsset, animId, buffer, static_cast(numKeyframes), scaleData, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT); if ( scaleAccessor ) { animRef->Parameters.scale = scaleAccessor; } @@ -923,7 +923,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, RefmRotationKeys[frameIndex].mValue.w; } - Ref rotAccessor = ExportData(mAsset, animId, buffer, numKeyframes, rotationData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT); + Ref rotAccessor = ExportData(mAsset, animId, buffer, static_cast(numKeyframes), rotationData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT); if ( rotAccessor ) { animRef->Parameters.rotation = rotAccessor; } From 63d3655f1b8b73e3c15799e061baaa8fa95e086f Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Thu, 24 Aug 2017 17:15:48 -0400 Subject: [PATCH 015/490] Duplicate gltfImporter as gltf2Importer; Include glTF2 importer in CMake List --- code/CMakeLists.txt | 2 + code/ImporterRegistry.cpp | 4 +- code/glTF2Importer.cpp | 680 ++++++++++++++++++++++++++++++++++++++ code/glTF2Importer.h | 91 +++++ 4 files changed, 775 insertions(+), 2 deletions(-) create mode 100644 code/glTF2Importer.cpp create mode 100644 code/glTF2Importer.h diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 236e6d7ef..0e7aad6cc 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -665,6 +665,8 @@ ADD_ASSIMP_IMPORTER( GLTF glTF2Asset.inl glTF2AssetWriter.h glTF2AssetWriter.inl + glTF2Importer.cpp + glTF2Importer.h glTF2Exporter.h glTF2Exporter.cpp ) diff --git a/code/ImporterRegistry.cpp b/code/ImporterRegistry.cpp index afc308075..2b5d9c1a0 100644 --- a/code/ImporterRegistry.cpp +++ b/code/ImporterRegistry.cpp @@ -181,7 +181,7 @@ corresponding preprocessor flag to selectively disable formats. # include "AssbinLoader.h" #endif #ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER -# include "glTFImporter.h" +# include "glTF2Importer.h" #endif #ifndef ASSIMP_BUILD_NO_C4D_IMPORTER # include "C4DImporter.h" @@ -335,7 +335,7 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out) out.push_back( new AssbinImporter() ); #endif #if ( !defined ASSIMP_BUILD_NO_GLTF_IMPORTER ) - out.push_back( new glTFImporter() ); + out.push_back( new glTF2Importer() ); #endif #if ( !defined ASSIMP_BUILD_NO_C4D_IMPORTER ) out.push_back( new C4DImporter() ); diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp new file mode 100644 index 000000000..bd4f42600 --- /dev/null +++ b/code/glTF2Importer.cpp @@ -0,0 +1,680 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER + +#include "glTF2Importer.h" +#include "StringComparison.h" +#include "StringUtils.h" + +#include +#include +#include +#include +#include + +#include + +#include "MakeVerboseFormat.h" + +#include "glTF2Asset.h" +// This is included here so WriteLazyDict's definition is found. +#include "glTF2AssetWriter.h" + +using namespace Assimp; +using namespace glTF2; + + +// +// glTF2Importer +// + +static const aiImporterDesc desc = { + "glTF2 Importer", + "", + "", + "", + aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportCompressedFlavour + | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental, + 0, + 0, + 0, + 0, + "gltf glb" +}; + +glTF2Importer::glTF2Importer() +: BaseImporter() +, meshOffsets() +, embeddedTexIdxs() +, mScene( NULL ) { + // empty +} + +glTF2Importer::~glTF2Importer() { + // empty +} + +const aiImporterDesc* glTF2Importer::GetInfo() const +{ + return &desc; +} + +bool glTF2Importer::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const +{ + const std::string& extension = GetExtension(pFile); + + if (extension == "gltf" || extension == "glb") + return true; + + if ((checkSig || !extension.length()) && pIOHandler) { + char buffer[4]; + + std::unique_ptr pStream(pIOHandler->Open(pFile)); + if (pStream && pStream->Read(buffer, sizeof(buffer), 1) == 1) { + if (memcmp(buffer, AI_GLB_MAGIC_NUMBER, sizeof(buffer)) == 0) { + return true; // Has GLB header + } + else if (memcmp(buffer, "{\r\n ", sizeof(buffer)) == 0 + || memcmp(buffer, "{\n ", sizeof(buffer)) == 0) { + // seems a JSON file, and we're the only format that can read them + return true; + } + } + } + + return false; +} + + + +//static void CopyValue(const glTF2::vec3& v, aiColor3D& out) +//{ +// out.r = v[0]; out.g = v[1]; out.b = v[2]; +//} + +static void CopyValue(const glTF2::vec4& v, aiColor4D& out) +{ + out.r = v[0]; out.g = v[1]; out.b = v[2]; out.a = v[3]; +} + +static void CopyValue(const glTF2::vec4& v, aiColor3D& out) +{ + out.r = v[0]; out.g = v[1]; out.b = v[2]; +} + +static void CopyValue(const glTF2::vec3& v, aiVector3D& out) +{ + out.x = v[0]; out.y = v[1]; out.z = v[2]; +} + +static void CopyValue(const glTF2::vec4& v, aiQuaternion& out) +{ + out.x = v[0]; out.y = v[1]; out.z = v[2]; out.w = v[3]; +} + +static void CopyValue(const glTF2::mat4& v, aiMatrix4x4& o) +{ + o.a1 = v[ 0]; o.b1 = v[ 1]; o.c1 = v[ 2]; o.d1 = v[ 3]; + o.a2 = v[ 4]; o.b2 = v[ 5]; o.c2 = v[ 6]; o.d2 = v[ 7]; + o.a3 = v[ 8]; o.b3 = v[ 9]; o.c3 = v[10]; o.d3 = v[11]; + o.a4 = v[12]; o.b4 = v[13]; o.c4 = v[14]; o.d4 = v[15]; +} + +inline void SetMaterialColorProperty(std::vector& embeddedTexIdxs, Asset& r, glTF2::TexProperty prop, aiMaterial* mat, + aiTextureType texType, const char* pKey, unsigned int type, unsigned int idx) +{ + if (prop.texture) { + if (prop.texture->source) { + aiString uri(prop.texture->source->uri); + + int texIdx = embeddedTexIdxs[prop.texture->source.GetIndex()]; + if (texIdx != -1) { // embedded + // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture) + uri.data[0] = '*'; + uri.length = 1 + ASSIMP_itoa10(uri.data + 1, MAXLEN - 1, texIdx); + } + + mat->AddProperty(&uri, _AI_MATKEY_TEXTURE_BASE, texType, 0); + } + } + else { + aiColor4D col; + CopyValue(prop.color, col); + mat->AddProperty(&col, 1, pKey, type, idx); + } +} + +void glTF2Importer::ImportMaterials(glTF2::Asset& r) +{ + mScene->mNumMaterials = unsigned(r.materials.Size()); + mScene->mMaterials = new aiMaterial*[mScene->mNumMaterials]; + + for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) { + aiMaterial* aimat = mScene->mMaterials[i] = new aiMaterial(); + + Material& mat = r.materials[i]; + + /*if (!mat.name.empty())*/ { + aiString str(mat.id /*mat.name*/); + aimat->AddProperty(&str, AI_MATKEY_NAME); + } + + SetMaterialColorProperty(embeddedTexIdxs, r, mat.diffuse, aimat, aiTextureType_DIFFUSE, AI_MATKEY_COLOR_DIFFUSE); + SetMaterialColorProperty(embeddedTexIdxs, r, mat.specular, aimat, aiTextureType_SPECULAR, AI_MATKEY_COLOR_SPECULAR); + SetMaterialColorProperty(embeddedTexIdxs, r, mat.ambient, aimat, aiTextureType_AMBIENT, AI_MATKEY_COLOR_AMBIENT); + + if (mat.shininess > 0.f) { + aimat->AddProperty(&mat.shininess, 1, AI_MATKEY_SHININESS); + } + } +} + + +static inline void SetFace(aiFace& face, int a) +{ + face.mNumIndices = 1; + face.mIndices = new unsigned int[1]; + face.mIndices[0] = a; +} + +static inline void SetFace(aiFace& face, int a, int b) +{ + face.mNumIndices = 2; + face.mIndices = new unsigned int[2]; + face.mIndices[0] = a; + face.mIndices[1] = b; +} + +static inline void SetFace(aiFace& face, int a, int b, int c) +{ + face.mNumIndices = 3; + face.mIndices = new unsigned int[3]; + face.mIndices[0] = a; + face.mIndices[1] = b; + face.mIndices[2] = c; +} + +static inline bool CheckValidFacesIndices(aiFace* faces, unsigned nFaces, unsigned nVerts) +{ + for (unsigned i = 0; i < nFaces; ++i) { + for (unsigned j = 0; j < faces[i].mNumIndices; ++j) { + unsigned idx = faces[i].mIndices[j]; + if (idx >= nVerts) + return false; + } + } + return true; +} + +void glTF2Importer::ImportMeshes(glTF2::Asset& r) +{ + std::vector meshes; + + unsigned int k = 0; + + for (unsigned int m = 0; m < r.meshes.Size(); ++m) { + Mesh& mesh = r.meshes[m]; + + // Check if mesh extensions is used + if(mesh.Extension.size() > 0) + { + for(Mesh::SExtension* cur_ext : mesh.Extension) + { +#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC + if(cur_ext->Type == Mesh::SExtension::EType::Compression_Open3DGC) + { + // Limitations for meshes when using Open3DGC-compression. + // It's a current limitation of sp... Specification have not this part still - about mesh compression. Why only one primitive? + // Because glTF is very flexibly. But in fact it ugly flexible. Every primitive can has own set of accessors and accessors can + // point to a-a-a-a-any part of buffer (through bufferview of course) and even to another buffer. We know that "Open3DGC-compression" + // is applicable only to part of buffer. As we can't guaranty continuity of the data for decoder, we will limit quantity of primitives. + // Yes indices, coordinates etc. still can br stored in different buffers, but with current specification it's a exporter problem. + // Also primitive can has only one of "POSITION", "NORMAL" and less then "AI_MAX_NUMBER_OF_TEXTURECOORDS" of "TEXCOORD". All accessor + // of primitive must point to one continuous region of the buffer. + if(mesh.primitives.size() > 2) throw DeadlyImportError("GLTF: When using Open3DGC compression then only one primitive per mesh are allowed."); + + Mesh::SCompression_Open3DGC* o3dgc_ext = (Mesh::SCompression_Open3DGC*)cur_ext; + Ref buf = r.buffers.Get(o3dgc_ext->Buffer); + + buf->EncodedRegion_SetCurrent(mesh.id); + } + else +#endif + { + throw DeadlyImportError("GLTF: Can not import mesh: unknown mesh extension (code: \"" + to_string(cur_ext->Type) + + "\"), only Open3DGC is supported."); + } + } + }// if(mesh.Extension.size() > 0) + + meshOffsets.push_back(k); + k += unsigned(mesh.primitives.size()); + + for (unsigned int p = 0; p < mesh.primitives.size(); ++p) { + Mesh::Primitive& prim = mesh.primitives[p]; + + aiMesh* aim = new aiMesh(); + meshes.push_back(aim); + + aim->mName = mesh.id; + if (mesh.primitives.size() > 1) { + size_t& len = aim->mName.length; + aim->mName.data[len] = '-'; + len += 1 + ASSIMP_itoa10(aim->mName.data + len + 1, unsigned(MAXLEN - len - 1), p); + } + + switch (prim.mode) { + case PrimitiveMode_POINTS: + aim->mPrimitiveTypes |= aiPrimitiveType_POINT; + break; + + case PrimitiveMode_LINES: + case PrimitiveMode_LINE_LOOP: + case PrimitiveMode_LINE_STRIP: + aim->mPrimitiveTypes |= aiPrimitiveType_LINE; + break; + + case PrimitiveMode_TRIANGLES: + case PrimitiveMode_TRIANGLE_STRIP: + case PrimitiveMode_TRIANGLE_FAN: + aim->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + break; + } + + Mesh::Primitive::Attributes& attr = prim.attributes; + + if (attr.position.size() > 0 && attr.position[0]) { + aim->mNumVertices = attr.position[0]->count; + attr.position[0]->ExtractData(aim->mVertices); + } + + if (attr.normal.size() > 0 && attr.normal[0]) attr.normal[0]->ExtractData(aim->mNormals); + + for (size_t tc = 0; tc < attr.texcoord.size() && tc < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++tc) { + attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc]); + aim->mNumUVComponents[tc] = attr.texcoord[tc]->GetNumComponents(); + + aiVector3D* values = aim->mTextureCoords[tc]; + for (unsigned int i = 0; i < aim->mNumVertices; ++i) { + values[i].y = 1 - values[i].y; // Flip Y coords + } + } + + + if (prim.indices) { + aiFace* faces = 0; + unsigned int nFaces = 0; + + unsigned int count = prim.indices->count; + + Accessor::Indexer data = prim.indices->GetIndexer(); + ai_assert(data.IsValid()); + + switch (prim.mode) { + case PrimitiveMode_POINTS: { + nFaces = count; + faces = new aiFace[nFaces]; + for (unsigned int i = 0; i < count; ++i) { + SetFace(faces[i], data.GetUInt(i)); + } + break; + } + + case PrimitiveMode_LINES: { + nFaces = count / 2; + faces = new aiFace[nFaces]; + for (unsigned int i = 0; i < count; i += 2) { + SetFace(faces[i / 2], data.GetUInt(i), data.GetUInt(i + 1)); + } + break; + } + + case PrimitiveMode_LINE_LOOP: + case PrimitiveMode_LINE_STRIP: { + nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0); + faces = new aiFace[nFaces]; + SetFace(faces[0], data.GetUInt(0), data.GetUInt(1)); + for (unsigned int i = 2; i < count; ++i) { + SetFace(faces[i - 1], faces[i - 2].mIndices[1], data.GetUInt(i)); + } + if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop + SetFace(faces[count - 1], faces[count - 2].mIndices[1], faces[0].mIndices[0]); + } + break; + } + + case PrimitiveMode_TRIANGLES: { + nFaces = count / 3; + faces = new aiFace[nFaces]; + for (unsigned int i = 0; i < count; i += 3) { + SetFace(faces[i / 3], data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2)); + } + break; + } + case PrimitiveMode_TRIANGLE_STRIP: { + nFaces = count - 2; + faces = new aiFace[nFaces]; + SetFace(faces[0], data.GetUInt(0), data.GetUInt(1), data.GetUInt(2)); + for (unsigned int i = 3; i < count; ++i) { + SetFace(faces[i - 2], faces[i - 1].mIndices[1], faces[i - 1].mIndices[2], data.GetUInt(i)); + } + break; + } + case PrimitiveMode_TRIANGLE_FAN: + nFaces = count - 2; + faces = new aiFace[nFaces]; + SetFace(faces[0], data.GetUInt(0), data.GetUInt(1), data.GetUInt(2)); + for (unsigned int i = 3; i < count; ++i) { + SetFace(faces[i - 2], faces[0].mIndices[0], faces[i - 1].mIndices[2], data.GetUInt(i)); + } + break; + } + + if (faces) { + aim->mFaces = faces; + aim->mNumFaces = nFaces; + ai_assert(CheckValidFacesIndices(faces, nFaces, aim->mNumVertices)); + } + } + + + if (prim.material) { + aim->mMaterialIndex = prim.material.GetIndex(); + } + } + } + + meshOffsets.push_back(k); + + CopyVector(meshes, mScene->mMeshes, mScene->mNumMeshes); +} + +void glTF2Importer::ImportCameras(glTF2::Asset& r) +{ + if (!r.cameras.Size()) return; + + mScene->mNumCameras = r.cameras.Size(); + mScene->mCameras = new aiCamera*[r.cameras.Size()]; + + for (size_t i = 0; i < r.cameras.Size(); ++i) { + Camera& cam = r.cameras[i]; + + aiCamera* aicam = mScene->mCameras[i] = new aiCamera(); + + if (cam.type == Camera::Perspective) { + + aicam->mAspect = cam.cameraProperties.perspective.aspectRatio; + aicam->mHorizontalFOV = cam.cameraProperties.perspective.yfov * aicam->mAspect; + aicam->mClipPlaneFar = cam.cameraProperties.perspective.zfar; + aicam->mClipPlaneNear = cam.cameraProperties.perspective.znear; + } + else { + // assimp does not support orthographic cameras + } + } +} + +void glTF2Importer::ImportLights(glTF2::Asset& r) +{ + if (!r.lights.Size()) return; + + mScene->mNumLights = r.lights.Size(); + mScene->mLights = new aiLight*[r.lights.Size()]; + + for (size_t i = 0; i < r.lights.Size(); ++i) { + Light& l = r.lights[i]; + + aiLight* ail = mScene->mLights[i] = new aiLight(); + + switch (l.type) { + case Light::Type_directional: + ail->mType = aiLightSource_DIRECTIONAL; break; + + case Light::Type_spot: + ail->mType = aiLightSource_SPOT; break; + + case Light::Type_ambient: + ail->mType = aiLightSource_AMBIENT; break; + + default: // Light::Type_point + ail->mType = aiLightSource_POINT; break; + } + + CopyValue(l.color, ail->mColorAmbient); + CopyValue(l.color, ail->mColorDiffuse); + CopyValue(l.color, ail->mColorSpecular); + + ail->mAngleOuterCone = l.falloffAngle; + ail->mAngleInnerCone = l.falloffExponent; // TODO fix this, it does not look right at all + + ail->mAttenuationConstant = l.constantAttenuation; + ail->mAttenuationLinear = l.linearAttenuation; + ail->mAttenuationQuadratic = l.quadraticAttenuation; + } +} + + +aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector& meshOffsets, glTF2::Ref& ptr) +{ + Node& node = *ptr; + + aiNode* ainode = new aiNode(node.id); + + if (!node.children.empty()) { + ainode->mNumChildren = unsigned(node.children.size()); + ainode->mChildren = new aiNode*[ainode->mNumChildren]; + + for (unsigned int i = 0; i < ainode->mNumChildren; ++i) { + aiNode* child = ImportNode(pScene, r, meshOffsets, node.children[i]); + child->mParent = ainode; + ainode->mChildren[i] = child; + } + } + + aiMatrix4x4& matrix = ainode->mTransformation; + if (node.matrix.isPresent) { + CopyValue(node.matrix.value, matrix); + } + else { + if (node.translation.isPresent) { + aiVector3D trans; + CopyValue(node.translation.value, trans); + aiMatrix4x4 t; + aiMatrix4x4::Translation(trans, t); + matrix = t * matrix; + } + + if (node.scale.isPresent) { + aiVector3D scal(1.f); + CopyValue(node.scale.value, scal); + aiMatrix4x4 s; + aiMatrix4x4::Scaling(scal, s); + matrix = s * matrix; + } + + + if (node.rotation.isPresent) { + aiQuaternion rot; + CopyValue(node.rotation.value, rot); + matrix = aiMatrix4x4(rot.GetMatrix()) * matrix; + } + } + + if (!node.meshes.empty()) { + int count = 0; + for (size_t i = 0; i < node.meshes.size(); ++i) { + int idx = node.meshes[i].GetIndex(); + count += meshOffsets[idx + 1] - meshOffsets[idx]; + } + + ainode->mNumMeshes = count; + ainode->mMeshes = new unsigned int[count]; + + int k = 0; + for (size_t i = 0; i < node.meshes.size(); ++i) { + int idx = node.meshes[i].GetIndex(); + for (unsigned int j = meshOffsets[idx]; j < meshOffsets[idx + 1]; ++j, ++k) { + ainode->mMeshes[k] = j; + } + } + } + + if (node.camera) { + pScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName; + } + + if (node.light) { + pScene->mLights[node.light.GetIndex()]->mName = ainode->mName; + } + + return ainode; +} + +void glTF2Importer::ImportNodes(glTF2::Asset& r) +{ + if (!r.scene) return; + + std::vector< Ref > rootNodes = r.scene->nodes; + + // The root nodes + unsigned int numRootNodes = unsigned(rootNodes.size()); + if (numRootNodes == 1) { // a single root node: use it + mScene->mRootNode = ImportNode(mScene, r, meshOffsets, rootNodes[0]); + } + else if (numRootNodes > 1) { // more than one root node: create a fake root + aiNode* root = new aiNode("ROOT"); + root->mChildren = new aiNode*[numRootNodes]; + for (unsigned int i = 0; i < numRootNodes; ++i) { + aiNode* node = ImportNode(mScene, r, meshOffsets, rootNodes[i]); + node->mParent = root; + root->mChildren[root->mNumChildren++] = node; + } + mScene->mRootNode = root; + } + + //if (!mScene->mRootNode) { + // mScene->mRootNode = new aiNode("EMPTY"); + //} +} + +void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset& r) +{ + embeddedTexIdxs.resize(r.images.Size(), -1); + + int numEmbeddedTexs = 0; + for (size_t i = 0; i < r.images.Size(); ++i) { + if (r.images[i].HasData()) + numEmbeddedTexs += 1; + } + + if (numEmbeddedTexs == 0) + return; + + mScene->mTextures = new aiTexture*[numEmbeddedTexs]; + + // Add the embedded textures + for (size_t i = 0; i < r.images.Size(); ++i) { + Image img = r.images[i]; + if (!img.HasData()) continue; + + int idx = mScene->mNumTextures++; + embeddedTexIdxs[i] = idx; + + aiTexture* tex = mScene->mTextures[idx] = new aiTexture(); + + size_t length = img.GetDataLength(); + void* data = img.StealData(); + + tex->mWidth = static_cast(length); + tex->mHeight = 0; + tex->pcData = reinterpret_cast(data); + + if (!img.mimeType.empty()) { + const char* ext = strchr(img.mimeType.c_str(), '/') + 1; + if (ext) { + if (strcmp(ext, "jpeg") == 0) ext = "jpg"; + + size_t len = strlen(ext); + if (len <= 3) { + strcpy(tex->achFormatHint, ext); + } + } + } + } +} + +void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { + + this->mScene = pScene; + + // read the asset file + glTF2::Asset asset(pIOHandler); + asset.Load(pFile, GetExtension(pFile) == "glb"); + + + // + // Copy the data out + // + + ImportEmbeddedTextures(asset); + ImportMaterials(asset); + + ImportMeshes(asset); + + ImportCameras(asset); + ImportLights(asset); + + ImportNodes(asset); + + // TODO: it does not split the loaded vertices, should it? + //pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; + MakeVerboseFormatProcess process; + process.Execute(pScene); + + + if (pScene->mNumMeshes == 0) { + pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; + } +} + +#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER + diff --git a/code/glTF2Importer.h b/code/glTF2Importer.h new file mode 100644 index 000000000..6be7131d1 --- /dev/null +++ b/code/glTF2Importer.h @@ -0,0 +1,91 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef AI_GLTF2IMPORTER_H_INC +#define AI_GLTF2IMPORTER_H_INC + +#include "BaseImporter.h" +#include + +struct aiNode; + + +namespace glTF2 +{ + class Asset; +} + +namespace Assimp { + +/** + * Load the glTF2 format. + * https://github.com/KhronosGroup/glTF/tree/master/specification + */ +class glTF2Importer : public BaseImporter{ +public: + glTF2Importer(); + virtual ~glTF2Importer(); + virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig ) const; + +protected: + virtual const aiImporterDesc* GetInfo() const; + virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler ); + +private: + + std::vector meshOffsets; + + std::vector embeddedTexIdxs; + + aiScene* mScene; + + void ImportEmbeddedTextures(glTF2::Asset& a); + void ImportMaterials(glTF2::Asset& a); + void ImportMeshes(glTF2::Asset& a); + void ImportCameras(glTF2::Asset& a); + void ImportLights(glTF2::Asset& a); + void ImportNodes(glTF2::Asset& a); + +}; + +} // Namespace assimp + +#endif // AI_GLTF2IMPORTER_H_INC + From f814acf33a553b38900b7bd2591d2864aeacdb34 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Thu, 24 Aug 2017 17:31:33 -0400 Subject: [PATCH 016/490] Update glTF2 Asset to use indexes --- code/glTF2Asset.h | 6 +- code/glTF2Asset.inl | 311 +++++++++++--------------------------------- 2 files changed, 77 insertions(+), 240 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 52bc18268..2e7a694d8 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -232,7 +232,7 @@ namespace glTF2 case ComponentType_UNSIGNED_BYTE: return 1; default: - throw DeadlyImportError("GLTF: Unsupported Component Type "+t); + throw DeadlyImportError("GLTF: Unsupported Component Type " + std::to_string(t)); } } @@ -629,7 +629,7 @@ namespace glTF2 float zfar; //! The floating-point distance to the far clipping plane. (required) float znear; //! The floating-point distance to the near clipping plane. (required) } ortographic; - }; + } cameraProperties; Camera() {} void Read(Value& obj, Asset& r); @@ -1146,7 +1146,7 @@ namespace glTF2 LazyDict samplers; LazyDict scenes; //LazyDict shaders; - LazyDict skins; + LazyDict skins; //LazyDict techniques; LazyDict textures; diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 09e647cca..268deb8a8 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -128,6 +128,12 @@ namespace { return (it != val.MemberEnd() && it->value.IsString()) ? &it->value : 0; } + inline Value* FindUInt(Value& val, const char* id) + { + Value::MemberIterator it = val.FindMember(id); + return (it != val.MemberEnd() && it->value.IsUint()) ? &it->value : 0; + } + inline Value* FindArray(Value& val, const char* id) { Value::MemberIterator it = val.FindMember(id); @@ -176,7 +182,7 @@ inline void LazyDict::AttachToDocument(Document& doc) } if (container) { - mDict = FindObject(*container, mDictId); + mDict = FindArray(*container, mDictId); } } @@ -189,13 +195,8 @@ inline void LazyDict::DetachFromDocument() template Ref LazyDict::Get(unsigned int i) { - return Ref(mObjs, i); -} -template -Ref LazyDict::Get(const char* id) -{ - id = T::TranslateId(mAsset, id); + std::string id = std::string(mDictId) + "[" + std::to_string(i) + "]"; typename Dict::iterator it = mObjsById.find(id); if (it != mObjsById.end()) { // already created? @@ -207,19 +208,21 @@ Ref LazyDict::Get(const char* id) throw DeadlyImportError("GLTF: Missing section \"" + std::string(mDictId) + "\""); } - Value::MemberIterator obj = mDict->FindMember(id); - if (obj == mDict->MemberEnd()) { - throw DeadlyImportError("GLTF: Missing object with id \"" + std::string(id) + "\" in \"" + mDictId + "\""); - } - if (!obj->value.IsObject()) { - throw DeadlyImportError("GLTF: Object with id \"" + std::string(id) + "\" is not a JSON object"); + if (!mDict->IsArray()) { + throw DeadlyImportError("GLTF: Field is not an array \"" + std::string(mDictId) + "\""); + } + + Value& obj = mDict->operator[](i); + + if (!obj.IsObject()) { + throw DeadlyImportError("GLTF: Object at index \"" + std::to_string(i) + "\" is not a JSON object"); } - // create an instance of the given type T* inst = new T(); inst->id = id; - ReadMember(obj->value, "name", inst->name); - inst->Read(obj->value, mAsset); + ReadMember(obj, "name", inst->name); + inst->Read(obj, mAsset); + return Add(inst); } @@ -437,9 +440,9 @@ inline void Buffer::Grow(size_t amount) inline void BufferView::Read(Value& obj, Asset& r) { - const char* bufferId = MemberOrDefault(obj, "buffer", 0); - if (bufferId) { - buffer = r.buffers.Get(bufferId); + + if (Value* bufferVal = FindUInt(obj, "buffer")) { + buffer = r.buffers.Get(bufferVal->GetUint()); } byteOffset = MemberOrDefault(obj, "byteOffset", 0u); @@ -452,9 +455,9 @@ inline void BufferView::Read(Value& obj, Asset& r) inline void Accessor::Read(Value& obj, Asset& r) { - const char* bufferViewId = MemberOrDefault(obj, "bufferView", 0); - if (bufferViewId) { - bufferView = r.bufferViews.Get(bufferViewId); + + if (Value* bufferViewVal = FindUInt(obj, "bufferView")) { + bufferView = r.bufferViews.Get(bufferViewVal->GetUint()); } byteOffset = MemberOrDefault(obj, "byteOffset", 0u); @@ -611,9 +614,8 @@ inline void Image::Read(Value& obj, Asset& r) ReadMember(*ext, "mimeType", mimeType); - const char* bufferViewId; - if (ReadMember(*ext, "bufferView", bufferViewId)) { - Ref bv = r.bufferViews.Get(bufferViewId); + if (Value* bufferViewVal = FindUInt(*ext, "bufferView")) { + Ref bv = r.bufferViews.Get(bufferViewVal->GetUint()); if (bv) { mDataLength = bv->byteLength; mData = new uint8_t[mDataLength]; @@ -687,23 +689,22 @@ inline void Sampler::SetDefaults() inline void Texture::Read(Value& obj, Asset& r) { - const char* sourcestr; - if (ReadMember(obj, "source", sourcestr)) { - source = r.images.Get(sourcestr); + if (Value* sourceVal = FindUInt(obj, "source")) { + source = r.images.Get(sourceVal->GetUint()); } - const char* samplerstr; - if (ReadMember(obj, "sampler", samplerstr)) { - sampler = r.samplers.Get(samplerstr); + if (Value* samplerVal = FindUInt(obj, "sampler")) { + sampler = r.samplers.Get(samplerVal->GetUint()); } } namespace { inline void ReadMaterialProperty(Asset& r, Value& vals, const char* propName, TexProperty& out) { + //@TODO: update this format if (Value* prop = FindMember(vals, propName)) { - if (prop->IsString()) { - out.texture = r.textures.Get(prop->GetString()); + if (prop->IsUint()) { + out.texture = r.textures.Get(prop->GetUint()); } else { ReadValue(*prop, out.color); @@ -824,22 +825,23 @@ inline void Mesh::Read(Value& pJSON_Object, Asset& pAsset_Root) // Valid attribute semantics include POSITION, NORMAL, TEXCOORD, COLOR, JOINT, JOINTMATRIX, // and WEIGHT.Attribute semantics can be of the form[semantic]_[set_index], e.g., TEXCOORD_0, TEXCOORD_1, etc. + //@TODO: update this int undPos = 0; Mesh::AccessorList* vec = 0; if (GetAttribVector(prim, attr, vec, undPos)) { size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0; if ((*vec).size() <= idx) (*vec).resize(idx + 1); - (*vec)[idx] = pAsset_Root.accessors.Get(it->value.GetString()); + (*vec)[idx] = pAsset_Root.accessors.Get(it->value.GetUint()); } } } - if (Value* indices = FindString(primitive, "indices")) { - prim.indices = pAsset_Root.accessors.Get(indices->GetString()); + if (Value* indices = FindUInt(primitive, "indices")) { + prim.indices = pAsset_Root.accessors.Get(indices->GetUint()); } - if (Value* material = FindString(primitive, "material")) { - prim.material = pAsset_Root.materials.Get(material->GetString()); + if (Value* material = FindUInt(primitive, "material")) { + prim.material = pAsset_Root.materials.Get(material->GetUint()); } } } @@ -914,177 +916,6 @@ mr_skip_extensions: return;// After label some operators must be present. } -#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC -inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC& pCompression_Open3DGC, Asset& pAsset_Root) -{ -typedef unsigned short IndicesType;///< \sa glTFExporter::ExportMeshes. - -o3dgc::SC3DMCDecoder decoder; -o3dgc::IndexedFaceSet ifs; -o3dgc::BinaryStream bstream; -uint8_t* decoded_data; -size_t decoded_data_size = 0; -Ref buf = pAsset_Root.buffers.Get(pCompression_Open3DGC.Buffer); - - // Read data from buffer and place it in BinaryStream for decoder. - // Just "Count" because always is used type equivalent to uint8_t. - bstream.LoadFromBuffer(&buf->GetPointer()[pCompression_Open3DGC.Offset], static_cast(pCompression_Open3DGC.Count)); - - // After decoding header we can get size of primitives. - if(decoder.DecodeHeader(ifs, bstream) != o3dgc::O3DGC_OK) throw DeadlyImportError("GLTF: can not decode Open3DGC header."); - - /****************** Get sizes of arrays and check sizes ******************/ - // Note. See "Limitations for meshes when using Open3DGC-compression". - - // Indices - size_t size_coordindex = ifs.GetNCoordIndex() * 3;// See float attributes note. - - if(primitives[0].indices->count != size_coordindex) - throw DeadlyImportError("GLTF: Open3DGC. Compressed indices count (" + std::to_string(size_coordindex) + - ") not equal to uncompressed (" + std::to_string(primitives[0].indices->count) + ")."); - - size_coordindex *= sizeof(IndicesType); - // Coordinates - size_t size_coord = ifs.GetNCoord();// See float attributes note. - - if(primitives[0].attributes.position[0]->count != size_coord) - throw DeadlyImportError("GLTF: Open3DGC. Compressed positions count (" + std::to_string(size_coord) + - ") not equal to uncompressed (" + std::to_string(primitives[0].attributes.position[0]->count) + ")."); - - size_coord *= 3 * sizeof(float); - // Normals - size_t size_normal = ifs.GetNNormal();// See float attributes note. - - if(primitives[0].attributes.normal[0]->count != size_normal) - throw DeadlyImportError("GLTF: Open3DGC. Compressed normals count (" + std::to_string(size_normal) + - ") not equal to uncompressed (" + std::to_string(primitives[0].attributes.normal[0]->count) + ")."); - - size_normal *= 3 * sizeof(float); - // Additional attributes. - std::vector size_floatattr; - std::vector size_intattr; - - size_floatattr.resize(ifs.GetNumFloatAttributes()); - size_intattr.resize(ifs.GetNumIntAttributes()); - - decoded_data_size = size_coordindex + size_coord + size_normal; - for(size_t idx = 0, idx_end = size_floatattr.size(), idx_texcoord = 0; idx < idx_end; idx++) - { - // size = number_of_elements * components_per_element * size_of_component. - // Note. But as you can see above, at first we are use this variable in meaning "count". After checking count of objects... - size_t tval = ifs.GetNFloatAttribute(static_cast(idx)); - - switch(ifs.GetFloatAttributeType(static_cast(idx))) - { - case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_TEXCOORD: - // Check situation when encoded data contain texture coordinates but primitive not. - if(idx_texcoord < primitives[0].attributes.texcoord.size()) - { - if(primitives[0].attributes.texcoord[idx]->count != tval) - throw DeadlyImportError("GLTF: Open3DGC. Compressed texture coordinates count (" + std::to_string(tval) + - ") not equal to uncompressed (" + std::to_string(primitives[0].attributes.texcoord[idx]->count) + ")."); - - idx_texcoord++; - } - else - { - ifs.SetNFloatAttribute(static_cast(idx), 0ul);// Disable decoding this attribute. - } - - break; - default: - throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: " + to_string(ifs.GetFloatAttributeType(static_cast(idx)))); - } - - tval *= ifs.GetFloatAttributeDim(static_cast(idx)) * sizeof(o3dgc::Real);// After checking count of objects we can get size of array. - size_floatattr[idx] = tval; - decoded_data_size += tval; - } - - for(size_t idx = 0, idx_end = size_intattr.size(); idx < idx_end; idx++) - { - // size = number_of_elements * components_per_element * size_of_component. See float attributes note. - size_t tval = ifs.GetNIntAttribute(static_cast(idx)); - switch( ifs.GetIntAttributeType(static_cast(idx) ) ) - { - case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_UNKOWN: - case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX: - case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_JOINT_ID: - case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX_BUFFER_ID: - break; - - default: - throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: " + to_string(ifs.GetIntAttributeType(static_cast(idx)))); - } - - tval *= ifs.GetIntAttributeDim(static_cast(idx)) * sizeof(long);// See float attributes note. - size_intattr[idx] = tval; - decoded_data_size += tval; - } - - // Create array for decoded data. - decoded_data = new uint8_t[decoded_data_size]; - - /****************** Set right array regions for decoder ******************/ - - auto get_buf_offset = [](Ref& pAccessor) -> size_t { return pAccessor->byteOffset + pAccessor->bufferView->byteOffset; }; - - // Indices - ifs.SetCoordIndex((IndicesType* const)(decoded_data + get_buf_offset(primitives[0].indices))); - // Coordinates - ifs.SetCoord((o3dgc::Real* const)(decoded_data + get_buf_offset(primitives[0].attributes.position[0]))); - // Normals - if(size_normal) - { - ifs.SetNormal((o3dgc::Real* const)(decoded_data + get_buf_offset(primitives[0].attributes.normal[0]))); - } - - for(size_t idx = 0, idx_end = size_floatattr.size(), idx_texcoord = 0; idx < idx_end; idx++) - { - switch(ifs.GetFloatAttributeType(static_cast(idx))) - { - case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_TEXCOORD: - if(idx_texcoord < primitives[0].attributes.texcoord.size()) - { - // See above about absent attributes. - ifs.SetFloatAttribute(static_cast(idx), (o3dgc::Real* const)(decoded_data + get_buf_offset(primitives[0].attributes.texcoord[idx]))); - idx_texcoord++; - } - - break; - default: - throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: " + to_string(ifs.GetFloatAttributeType(static_cast(idx)))); - } - } - - for(size_t idx = 0, idx_end = size_intattr.size(); idx < idx_end; idx++) { - switch(ifs.GetIntAttributeType(static_cast(idx))) { - case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_UNKOWN: - case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX: - case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_JOINT_ID: - case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX_BUFFER_ID: - break; - - // ifs.SetIntAttribute(idx, (long* const)(decoded_data + get_buf_offset(primitives[0].attributes.joint))); - default: - throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: " + to_string(ifs.GetIntAttributeType(static_cast(idx)))); - } - } - - // - // Decode data - // - if ( decoder.DecodePayload( ifs, bstream ) != o3dgc::O3DGC_OK ) { - throw DeadlyImportError( "GLTF: can not decode Open3DGC data." ); - } - - // Set encoded region for "buffer". - buf->EncodedRegion_Mark(pCompression_Open3DGC.Offset, pCompression_Open3DGC.Count, decoded_data, decoded_data_size, id); - // No. Do not delete "output_data". After calling "EncodedRegion_Mark" bufferView is owner of "output_data". - // "delete [] output_data;" -} -#endif - inline void Camera::Read(Value& obj, Asset& r) { type = MemberOrDefault(obj, "type", Camera::Perspective); @@ -1095,16 +926,16 @@ inline void Camera::Read(Value& obj, Asset& r) if (!it) throw DeadlyImportError("GLTF: Camera missing its parameters"); if (type == Camera::Perspective) { - perspective.aspectRatio = MemberOrDefault(*it, "aspectRatio", 0.f); - perspective.yfov = MemberOrDefault(*it, "yfov", 3.1415f/2.f); - perspective.zfar = MemberOrDefault(*it, "zfar", 100.f); - perspective.znear = MemberOrDefault(*it, "znear", 0.01f); + cameraProperties.perspective.aspectRatio = MemberOrDefault(*it, "aspectRatio", 0.f); + cameraProperties.perspective.yfov = MemberOrDefault(*it, "yfov", 3.1415f/2.f); + cameraProperties.perspective.zfar = MemberOrDefault(*it, "zfar", 100.f); + cameraProperties.perspective.znear = MemberOrDefault(*it, "znear", 0.01f); } else { - ortographic.xmag = MemberOrDefault(obj, "xmag", 1.f); - ortographic.ymag = MemberOrDefault(obj, "ymag", 1.f); - ortographic.zfar = MemberOrDefault(obj, "zfar", 100.f); - ortographic.znear = MemberOrDefault(obj, "znear", 0.01f); + cameraProperties.ortographic.xmag = MemberOrDefault(obj, "xmag", 1.f); + cameraProperties.ortographic.ymag = MemberOrDefault(obj, "ymag", 1.f); + cameraProperties.ortographic.zfar = MemberOrDefault(obj, "zfar", 100.f); + cameraProperties.ortographic.znear = MemberOrDefault(obj, "znear", 0.01f); } } @@ -1156,13 +987,14 @@ inline void Light::SetDefaults() inline void Node::Read(Value& obj, Asset& r) { + if (Value* children = FindArray(obj, "children")) { this->children.reserve(children->Size()); for (unsigned int i = 0; i < children->Size(); ++i) { Value& child = (*children)[i]; - if (child.IsString()) { + if (child.IsNumber()) { // get/create the child node - Ref chn = r.nodes.Get(child.GetString()); + Ref chn = r.nodes.Get(child.GetUint()); if (chn) this->children.push_back(chn); } } @@ -1185,15 +1017,15 @@ inline void Node::Read(Value& obj, Asset& r) this->meshes.reserve(numMeshes); for (unsigned i = 0; i < numMeshes; ++i) { - if ((*meshes)[i].IsString()) { - Ref mesh = r.meshes.Get((*meshes)[i].GetString()); + if ((*meshes)[i].IsNumber()) { + Ref mesh = r.meshes.Get((*meshes)[i].GetUint()); if (mesh) this->meshes.push_back(mesh); } } } - if (Value* camera = FindString(obj, "camera")) { - this->camera = r.cameras.Get(camera->GetString()); + if (Value* camera = FindUInt(obj, "camera")) { + this->camera = r.cameras.Get(camera->GetUint()); if (this->camera) this->camera->id = this->id; } @@ -1204,8 +1036,8 @@ inline void Node::Read(Value& obj, Asset& r) if (r.extensionsUsed.KHR_materials_common) { if (Value* ext = FindObject(*extensions, "KHR_materials_common")) { - if (Value* light = FindString(*ext, "light")) { - this->light = r.lights.Get(light->GetString()); + if (Value* light = FindUInt(*ext, "light")) { + this->light = r.lights.Get(light->GetUint()); } } @@ -1217,8 +1049,8 @@ inline void Scene::Read(Value& obj, Asset& r) { if (Value* array = FindArray(obj, "nodes")) { for (unsigned int i = 0; i < array->Size(); ++i) { - if (!(*array)[i].IsString()) continue; - Ref node = r.nodes.Get((*array)[i].GetString()); + if (!(*array)[i].IsNumber()) continue; + Ref node = r.nodes.Get((*array)[i].GetUint()); if (node) this->nodes.push_back(node); } @@ -1229,13 +1061,14 @@ inline void Scene::Read(Value& obj, Asset& r) inline void AssetMetadata::Read(Document& doc) { // read the version, etc. - float statedVersion = 0; + std::string statedVersion; + if (Value* obj = FindObject(doc, "asset")) { ReadMember(*obj, "copyright", copyright); ReadMember(*obj, "generator", generator); premultipliedAlpha = MemberOrDefault(*obj, "premultipliedAlpha", false); - statedVersion = MemberOrDefault(*obj, "version", 0); + statedVersion = MemberOrDefault(*obj, "version", "0.0"); if (Value* profile = FindObject(*obj, "profile")) { ReadMember(*profile, "api", this->profile.api); @@ -1243,14 +1076,16 @@ inline void AssetMetadata::Read(Document& doc) } } - version = std::max(statedVersion, version); + float statedFloatVersion = std::strtof(statedVersion.c_str(), 0); + + version = std::max(statedFloatVersion, version); if (version == 0) { - // if missing version, we'll assume version 1.0... - version = 1; + // if missing version, we'll assume version 2.0... + version = 2; } - if (version != 1) { + if (version != 2) { char msg[128]; ai_snprintf(msg, 128, "GLTF: Unsupported glTF version: %.1f", version); throw DeadlyImportError(msg); @@ -1361,12 +1196,14 @@ inline void Asset::Load(const std::string& pFile, bool isBinary) mDicts[i]->AttachToDocument(doc); } - - // Read the "scene" property, which specifies which scene to load // and recursively load everything referenced by it - if (Value* scene = FindString(doc, "scene")) { - this->scene = scenes.Get(scene->GetString()); + if (Value* scene = FindUInt(doc, "scene")) { + unsigned int sceneIndex = scene->GetUint(); + + Ref s = scenes.Get(sceneIndex); + + this->scene = s; } // Clean up From 47c7c3cf506166b483fd4e44e20a58cd1107e763 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Fri, 25 Aug 2017 16:09:07 -0400 Subject: [PATCH 017/490] Disambiguate Get methods --- code/glTF2Asset.h | 4 +-- code/glTF2Asset.inl | 59 +++++++++++++++++++++++++-------------------- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 2e7a694d8..2ec2b84eb 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -1046,9 +1046,9 @@ namespace glTF2 LazyDict(Asset& asset, const char* dictId, const char* extId = 0); ~LazyDict(); - Ref Get(const char* id); + Ref Retrieve(unsigned int i); + Ref Get(unsigned int i); - Ref Get(const std::string& pID) { return Get(pID.c_str()); } Ref Create(const char* id); Ref Create(const std::string& id) diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 268deb8a8..97e507ee6 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -194,6 +194,14 @@ inline void LazyDict::DetachFromDocument() template Ref LazyDict::Get(unsigned int i) +{ + + return Ref(mObjs, i); + +} + +template +Ref LazyDict::Retrieve(unsigned int i) { std::string id = std::string(mDictId) + "[" + std::to_string(i) + "]"; @@ -442,7 +450,7 @@ inline void BufferView::Read(Value& obj, Asset& r) { if (Value* bufferVal = FindUInt(obj, "buffer")) { - buffer = r.buffers.Get(bufferVal->GetUint()); + buffer = r.buffers.Retrieve(bufferVal->GetUint()); } byteOffset = MemberOrDefault(obj, "byteOffset", 0u); @@ -457,7 +465,7 @@ inline void Accessor::Read(Value& obj, Asset& r) { if (Value* bufferViewVal = FindUInt(obj, "bufferView")) { - bufferView = r.bufferViews.Get(bufferViewVal->GetUint()); + bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint()); } byteOffset = MemberOrDefault(obj, "byteOffset", 0u); @@ -615,7 +623,7 @@ inline void Image::Read(Value& obj, Asset& r) ReadMember(*ext, "mimeType", mimeType); if (Value* bufferViewVal = FindUInt(*ext, "bufferView")) { - Ref bv = r.bufferViews.Get(bufferViewVal->GetUint()); + Ref bv = r.bufferViews.Retrieve(bufferViewVal->GetUint()); if (bv) { mDataLength = bv->byteLength; mData = new uint8_t[mDataLength]; @@ -690,11 +698,11 @@ inline void Sampler::SetDefaults() inline void Texture::Read(Value& obj, Asset& r) { if (Value* sourceVal = FindUInt(obj, "source")) { - source = r.images.Get(sourceVal->GetUint()); + source = r.images.Retrieve(sourceVal->GetUint()); } if (Value* samplerVal = FindUInt(obj, "sampler")) { - sampler = r.samplers.Get(samplerVal->GetUint()); + sampler = r.samplers.Retrieve(samplerVal->GetUint()); } } @@ -704,7 +712,7 @@ namespace { //@TODO: update this format if (Value* prop = FindMember(vals, propName)) { if (prop->IsUint()) { - out.texture = r.textures.Get(prop->GetUint()); + out.texture = r.textures.Retrieve(prop->GetUint()); } else { ReadValue(*prop, out.color); @@ -820,7 +828,7 @@ inline void Mesh::Read(Value& pJSON_Object, Asset& pAsset_Root) if (Value* attrs = FindObject(primitive, "attributes")) { for (Value::MemberIterator it = attrs->MemberBegin(); it != attrs->MemberEnd(); ++it) { - if (!it->value.IsString()) continue; + if (!it->value.IsUint()) continue; const char* attr = it->name.GetString(); // Valid attribute semantics include POSITION, NORMAL, TEXCOORD, COLOR, JOINT, JOINTMATRIX, // and WEIGHT.Attribute semantics can be of the form[semantic]_[set_index], e.g., TEXCOORD_0, TEXCOORD_1, etc. @@ -831,17 +839,17 @@ inline void Mesh::Read(Value& pJSON_Object, Asset& pAsset_Root) if (GetAttribVector(prim, attr, vec, undPos)) { size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0; if ((*vec).size() <= idx) (*vec).resize(idx + 1); - (*vec)[idx] = pAsset_Root.accessors.Get(it->value.GetUint()); + (*vec)[idx] = pAsset_Root.accessors.Retrieve(it->value.GetUint()); } } } if (Value* indices = FindUInt(primitive, "indices")) { - prim.indices = pAsset_Root.accessors.Get(indices->GetUint()); + prim.indices = pAsset_Root.accessors.Retrieve(indices->GetUint()); } if (Value* material = FindUInt(primitive, "material")) { - prim.material = pAsset_Root.materials.Get(material->GetUint()); + prim.material = pAsset_Root.materials.Retrieve(material->GetUint()); } } } @@ -992,9 +1000,9 @@ inline void Node::Read(Value& obj, Asset& r) this->children.reserve(children->Size()); for (unsigned int i = 0; i < children->Size(); ++i) { Value& child = (*children)[i]; - if (child.IsNumber()) { + if (child.IsUint()) { // get/create the child node - Ref chn = r.nodes.Get(child.GetUint()); + Ref chn = r.nodes.Retrieve(child.GetUint()); if (chn) this->children.push_back(chn); } } @@ -1010,22 +1018,21 @@ inline void Node::Read(Value& obj, Asset& r) ReadMember(obj, "rotation", rotation); } - if (Value* meshes = FindArray(obj, "meshes")) { - unsigned numMeshes = (unsigned)meshes->Size(); + if (Value* mesh = FindUInt(obj, "mesh")) { + //unsigned numMeshes = (unsigned)meshes->Size(); + unsigned numMeshes = 1; - std::vector meshList; + //std::vector meshList; this->meshes.reserve(numMeshes); - for (unsigned i = 0; i < numMeshes; ++i) { - if ((*meshes)[i].IsNumber()) { - Ref mesh = r.meshes.Get((*meshes)[i].GetUint()); - if (mesh) this->meshes.push_back(mesh); - } - } + + Ref meshRef = r.meshes.Retrieve((*mesh).GetUint()); + + if (meshRef) this->meshes.push_back(meshRef); } if (Value* camera = FindUInt(obj, "camera")) { - this->camera = r.cameras.Get(camera->GetUint()); + this->camera = r.cameras.Retrieve(camera->GetUint()); if (this->camera) this->camera->id = this->id; } @@ -1037,7 +1044,7 @@ inline void Node::Read(Value& obj, Asset& r) if (Value* ext = FindObject(*extensions, "KHR_materials_common")) { if (Value* light = FindUInt(*ext, "light")) { - this->light = r.lights.Get(light->GetUint()); + this->light = r.lights.Retrieve(light->GetUint()); } } @@ -1049,8 +1056,8 @@ inline void Scene::Read(Value& obj, Asset& r) { if (Value* array = FindArray(obj, "nodes")) { for (unsigned int i = 0; i < array->Size(); ++i) { - if (!(*array)[i].IsNumber()) continue; - Ref node = r.nodes.Get((*array)[i].GetUint()); + if (!(*array)[i].IsUint()) continue; + Ref node = r.nodes.Retrieve((*array)[i].GetUint()); if (node) this->nodes.push_back(node); } @@ -1201,7 +1208,7 @@ inline void Asset::Load(const std::string& pFile, bool isBinary) if (Value* scene = FindUInt(doc, "scene")) { unsigned int sceneIndex = scene->GetUint(); - Ref s = scenes.Get(sceneIndex); + Ref s = scenes.Retrieve(sceneIndex); this->scene = s; } From 4d59dee5ea25b7f1a7512348de6bc80a09a6d3fa Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Fri, 25 Aug 2017 16:52:44 -0400 Subject: [PATCH 018/490] Cache retrieved items via an original index map --- code/glTF2Asset.h | 15 ++++++++------- code/glTF2Asset.inl | 11 +++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 2ec2b84eb..2b37d6a0e 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -382,6 +382,7 @@ namespace glTF2 struct Object { int index; //!< The index of this object within its property container + int oIndex; //!< The original index of this object defined in the JSON std::string id; //!< The globally unique ID used to reference this object std::string name; //!< The user-defined name of this object @@ -1025,14 +1026,14 @@ namespace glTF2 friend class Asset; friend class AssetWriter; - typedef typename std::gltf_unordered_map< std::string, unsigned int > Dict; + typedef typename std::gltf_unordered_map< unsigned int, unsigned int > Dict; - std::vector mObjs; //! The read objects - Dict mObjsById; //! The read objects accessible by id - const char* mDictId; //! ID of the dictionary object - const char* mExtId; //! ID of the extension defining the dictionary - Value* mDict; //! JSON dictionary object - Asset& mAsset; //! The asset instance + std::vector mObjs; //! The read objects + Dict mObjsByOIndex; //! The read objects accessible by original index + const char* mDictId; //! ID of the dictionary object + const char* mExtId; //! ID of the extension defining the dictionary + Value* mDict; //! JSON dictionary object + Asset& mAsset; //! The asset instance void AttachToDocument(Document& doc); void DetachFromDocument(); diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 97e507ee6..062d8318d 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -204,10 +204,8 @@ template Ref LazyDict::Retrieve(unsigned int i) { - std::string id = std::string(mDictId) + "[" + std::to_string(i) + "]"; - - typename Dict::iterator it = mObjsById.find(id); - if (it != mObjsById.end()) { // already created? + typename Dict::iterator it = mObjsByOIndex.find(i); + if (it != mObjsByOIndex.end()) {// already created? return Ref(mObjs, it->second); } @@ -227,7 +225,8 @@ Ref LazyDict::Retrieve(unsigned int i) } T* inst = new T(); - inst->id = id; + inst->id = std::string(mDictId) + "[" + std::to_string(i) + "]"; + inst->oIndex = i; ReadMember(obj, "name", inst->name); inst->Read(obj, mAsset); @@ -239,7 +238,7 @@ Ref LazyDict::Add(T* obj) { unsigned int idx = unsigned(mObjs.size()); mObjs.push_back(obj); - mObjsById[obj->id] = idx; + mObjsByOIndex[obj->oIndex] = idx; mAsset.mUsedIds[obj->id] = true; return Ref(mObjs, idx); } From 39172feb3e3e9bb0c185209ed996867414ff0f44 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Fri, 25 Aug 2017 17:58:41 -0400 Subject: [PATCH 019/490] Start reading pbr materials --- code/glTF2Asset.inl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 062d8318d..27fdd1c01 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -733,6 +733,12 @@ inline void Material::Read(Value& material, Asset& r) ReadMember(*values, "shininess", shininess); } + if (Value* values = FindObject(material, "pbrMetallicRoughness")) { + ReadMaterialProperty(r, *values, "baseColorFactor", this->diffuse); + } + + ReadMember(material, "doubleSided", doubleSided); + if (Value* extensions = FindObject(material, "extensions")) { if (r.extensionsUsed.KHR_materials_common) { if (Value* ext = FindObject(*extensions, "KHR_materials_common")) { From 67eb3b06087021490e43d6c74e05dcd88c6d921a Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Sun, 27 Aug 2017 23:47:31 -0400 Subject: [PATCH 020/490] temporarily disable gltf exporting of animations and skins --- code/glTF2Exporter.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 7daef7000..68f2f4420 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -128,7 +128,7 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai ExportScene(); - ExportAnimations(); + //ExportAnimations(); AssetWriter writer(*mAsset); @@ -414,7 +414,7 @@ Ref FindSkeletonRootJoint(Ref& skinRef) return parentNodeRef; } -void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref& meshRef, Ref& bufferRef, Ref& skinRef, std::vector& inverseBindMatricesData) +/*void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref& meshRef, Ref& bufferRef, Ref& skinRef, std::vector& inverseBindMatricesData) { if (aimesh->mNumBones < 1) { return; @@ -491,7 +491,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref& meshRef, RefHasBones()) { - ExportSkin(*mAsset, aim, m, b, skinRef, inverseBindMatricesData); - } + /*************** Skins ****************/ + /*if(aim->HasBones()) { + ExportSkin(*mAsset, aim, m, b, skinRef, inverseBindMatricesData); + }*/ /****************** Compression ******************/ ///TODO: animation: weights, joints. @@ -942,7 +942,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, Ref bufferRef = mAsset->buffers.Get(unsigned (0)); @@ -963,7 +963,7 @@ void glTF2Exporter::ExportAnimations() name = mAsset->FindUniqueID(name, "animation"); Ref animRef = mAsset->animations.Create(name); - /******************* Parameters ********************/ + // Parameters ExtractAnimationData(*mAsset, name, animRef, bufferRef, nodeChannel, anim->mTicksPerSecond); for (unsigned int j = 0; j < 3; ++j) { @@ -1011,7 +1011,7 @@ void glTF2Exporter::ExportAnimations() // } } // End: for-loop mNumAnimations -} +} */ #endif // ASSIMP_BUILD_NO_GLTF_EXPORTER From b42d785afebd313f8a531dc8cf832d80ecd50910 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Sun, 27 Aug 2017 23:47:54 -0400 Subject: [PATCH 021/490] Start managing and importing gltf2 pbr materials --- code/glTF2Asset.h | 40 +++++++++++++++++++++++++++++----------- code/glTF2Asset.inl | 19 +++++++++++++++---- 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 2b37d6a0e..5d99735b2 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -671,11 +671,18 @@ namespace glTF2 inline void SetData(uint8_t* data, size_t length, Asset& r); }; - //! Holds a material property that can be a texture or a color - struct TexProperty + struct ColorProperty + { + union { + vec4, vec3 + } color; + } + + //! Holds a material property that can be a texture or a color (fallback for glTF 1) + struct FallbackTexProperty { Ref texture; - vec4 color; + ColorProperty color; }; //! The material appearance of a primitive. @@ -694,16 +701,27 @@ namespace glTF2 Technique_CONSTANT }; - TexProperty ambient; - TexProperty diffuse; - TexProperty specular; - TexProperty emission; - Ref normal; + //PBR metallic roughness properties + ColorProperty baseColor; + Ref baseColorTexture; + Ref metallicRoughnessTexture; + float metallicFactor; + float roughnessFactor; + //other basic material properties + Ref normalTexture; + Ref occlusionTexture; + Ref emissiveTexture; + ColorProperty emissiveFactor; + std::string alphaMode; + float alphaCutoff; bool doubleSided; - bool transparent; - float transparency; - float shininess; + + //fallback material properties (compatible with non-pbr defintions) + FallbackTexProperty diffuse; + FallbackTexProperty emissive; + FallbackTexProperty specular; + Ref normal; Technique technique; diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 27fdd1c01..bf8cb5a36 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -725,18 +725,29 @@ inline void Material::Read(Value& material, Asset& r) SetDefaults(); if (Value* values = FindObject(material, "values")) { - ReadMaterialProperty(r, *values, "ambient", this->ambient); - ReadMaterialProperty(r, *values, "diffuse", this->diffuse); - ReadMaterialProperty(r, *values, "specular", this->specular); + ReadMember(*values, "transparency", transparency); - ReadMember(*values, "shininess", shininess); } if (Value* values = FindObject(material, "pbrMetallicRoughness")) { + //pbr + ReadMaterialProperty(r, *values, "baseColorFactor", this->baseColor); + ReadMaterialProperty(r, *values, "baseColorTexture", this->baseColorTexture); + + //non-pbr fallback ReadMaterialProperty(r, *values, "baseColorFactor", this->diffuse); + ReadMaterialProperty(r, *values, "baseColorTexture", this->diffuse); + + ReadMember(*values, "metallicFactor", metallicFactor); } + ReadMaterialProperty(r, *values, "normalTexture", this->normalTexture); + ReadMaterialProperty(r, *values, "normalTexture", this->normal); + ReadMaterialProperty(r, *values, "occlusionTexture", this->occlusionTexture); + ReadMaterialProperty(r, *values, "emissiveTexture", this->emissiveTexture); + ReadMember(*values, "metallicFactor", emissiveFactor); + ReadMember(material, "doubleSided", doubleSided); if (Value* extensions = FindObject(material, "extensions")) { From 6b4286abf69a6bdff733dcf88a13891c21892985 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Sun, 27 Aug 2017 23:50:22 -0400 Subject: [PATCH 022/490] check in gltf2 models to test directory Remove un-needed test models --- .../BoxTextured-glTF-Binary/BoxTextured.glb | Bin 0 -> 4696 bytes .../BoxTextured.gltf | 181 +++++++++++ .../BoxTextured.gltf | 197 ++++++++++++ .../BoxTextured0.bin | Bin 0 -> 840 bytes .../CesiumLogoFlat.png | Bin 0 -> 2433 bytes .../BoxTextured.gltf | 282 ++++++++++++++++++ .../BoxTextured0.bin | Bin 0 -> 840 bytes .../BoxTextured0.vert | 17 ++ .../BoxTextured1.frag | 29 ++ .../CesiumLogoFlat.png | Bin 0 -> 2433 bytes .../glTF2/BoxTextured-glTF/BoxTextured.gltf | 181 +++++++++++ .../glTF2/BoxTextured-glTF/BoxTextured0.bin | Bin 0 -> 840 bytes .../glTF2/BoxTextured-glTF/CesiumLogoFlat.png | Bin 0 -> 2433 bytes 13 files changed, 887 insertions(+) create mode 100644 test/models/glTF2/BoxTextured-glTF-Binary/BoxTextured.glb create mode 100644 test/models/glTF2/BoxTextured-glTF-Embedded/BoxTextured.gltf create mode 100644 test/models/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf create mode 100644 test/models/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured0.bin create mode 100644 test/models/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/CesiumLogoFlat.png create mode 100644 test/models/glTF2/BoxTextured-glTF-techniqueWebGL/BoxTextured.gltf create mode 100644 test/models/glTF2/BoxTextured-glTF-techniqueWebGL/BoxTextured0.bin create mode 100644 test/models/glTF2/BoxTextured-glTF-techniqueWebGL/BoxTextured0.vert create mode 100644 test/models/glTF2/BoxTextured-glTF-techniqueWebGL/BoxTextured1.frag create mode 100644 test/models/glTF2/BoxTextured-glTF-techniqueWebGL/CesiumLogoFlat.png create mode 100644 test/models/glTF2/BoxTextured-glTF/BoxTextured.gltf create mode 100644 test/models/glTF2/BoxTextured-glTF/BoxTextured0.bin create mode 100644 test/models/glTF2/BoxTextured-glTF/CesiumLogoFlat.png diff --git a/test/models/glTF2/BoxTextured-glTF-Binary/BoxTextured.glb b/test/models/glTF2/BoxTextured-glTF-Binary/BoxTextured.glb new file mode 100644 index 0000000000000000000000000000000000000000..03cd2acf941a0838722aa3e7243d33a8f459c22e GIT binary patch literal 4696 zcmbUj2{@GN`cEYsB}<5iNh)L+J7qbxXqXs8A%mG|WXu>dvXy-)rLt?GvUDV*bV!TN z*s_;#$i63}>?+&+|1r~XyXT(nbHC?#|9AV|{r%<{oQJhB8vp=yd;mb&3;<>*q(uM> z?d^@Bz_bEjIE*KTjHVFDFfEt?5`oY+)Q6iOtc_uEFdq!r8&C8E6>xb)*f}|vw<~xV zrlrWFykS}=17My+Hw;86I-YZs`_2WxTumj(q?Nfo0;Z)T z2RnvDnOd77Enr%3IheJPodFVQWq3*vrlkxPiT8BFyFw;lnFO#ln3f77dkh(m_F%^J zL=!+c%pA%B$^q>PT6h!5YaMZ+VzC&qEgo~0;l#y{fUk z)qbd#xn2Jj4bB)UxUr0xuML&tLN>=x&>kLmS1TeFciIyiIJ8zSXm5-G(St~~#`sdG zWN@MZ&2`ULpxTHYx#4*ZX`dV`gnj7Oiz`9^sn(A;(#?ZeDG&R6U zoh745DDagis%WaOan?)@%JQ$EYyQ^w|5$T%HEUo-R^m45IA{|8^+jai8(X)+gwU>m>#!d75AzzgsJ{2=fG0>BPH5D)@k2XNt-g~?Vf z5iT&$R?{PfRv@NBz_Ahh%C)zSgQ)Nr!rG{-t7~a_d1ZCgA!^_1>T2Rc?=Ops=$M1; zoo(b~!@xAl+4*UwXqm^AY3aowF*(@0KN2VBm%=k0?!CB#ja6yxEE2z(-V4IL+!xqo8h_A_Q|rwyqdP+k%`{E53RKB>CWM%;o)JAyxxcP z{XxZpN#(SMFW(={-sou7+k4tOyJE|RZ&iR**9=&KVQl=sxf`>}2S|T<;#ZhL^ct$!PlY`16PdQf zZNFpkCW$JnuM77#E>GiKAUzng%NekUiygiaODc9wvVC&b@jf3q;cBZ~n6p%BnP7yH zNOBE*yHeT=f0>P7y}nQDwddt3EAQSPxjZywT7{O!88Qp#ns}YYy+1q)qagSUHsH)@T0NmLHB1;CE6?2 zk{|fYh|QLFwu;>{=RW6tDj~`khqMZ{$Z?&9QA%~rN8N6B+b8TlrIVhcbUe9>TH{-!*Nu!bTe3}+>czz_ymcmX`E(5&8xkndIUn!8NAs|T9oPKt zFE~SYX$W&+j|WQE1lBst&b^7xWJj3uD0VI)R6SVvd@ab^gfu*u-)0`iQx6ecBm5^)w@56T^J2gSUvw_qUwY9wccdk zd(&7l{BWL?MnHQvUH7=DtOLDH??StDd6eAP5l>l?*=VZOm}tZ=1yk6=#Z4p=!v0jv z>O@$F+G&}tdnhV;;pk1xl=n;QCtR_E@_BG9SRGVZmG%By@~QaLKc0Ur@+h4mXl;VaA22% z)HbheBT1X)LQs{li;ureCZCF6Oa2;uc3jU%;K7kYsQJ>eqLfZ|nx25sg&S^)LK&vk z9UaXggxfYAydtBkL3XD$jeDqq+0KbPDvx{&7m9UCGyrTghE3v$d3ENnngIbtKQV`o zMTy~C1_ex}H4p!3=`MPV3BWJnfIuvxPh8LvkE-dEqEt~e_rKBzpQC*?$L=IM3X-WdKM;SnO2 zOK|7s+P(etq1izhGAM!a>>-OdQk90x-t@DVd_^UHIcsGmPghR4s{J(&p_+g7eG$@s zCK2xvM8)`1wKO6gBM{kHEoaL7nx4iR`&Kt=PnC))*f3ZHaU{ z(Z`-ePkl(`z;W5uqOz;{*mtIPJJMEtiwDKCLL|8Q=#2vTln|@nyIW+s_MQ$l3`piL zTNP5SGK4*z5ZyJopzdd0{=8edGZcQutfuCaG{GpjXIH;oN^ai7m(3loBPjK^S~FEv zNC8<%4RaU~axqWlN3s6mf)?=v)A4v8lFE+jp+fonqk?G*H;W2lNr=x@CdkyHW7p;f zII6L?tycVYUz;D}tiGSNOwU^$Cej_9-Gp@)EBf!2FDKGwoD_slpen?p?nQ7F2b6Lj zjjf{Rxz)c5n=miFEZOBb?~&VL+7M7 zZLO@mF(qYIaX0(QF;<0@dyK^Lw=Eo((Y&28*PP_xyRXo1H|K)(ahY!o=VvTeyn1Ba zy?PX1B0c(7%(6z`H&yvMaHX8J-j`RrG&RxoPE03Dq6>8Z^Qb*H_r6~>^g~*(UXoxdlNdhK+VOk=Cp!ifNI#CMeS-$)D?AWqi`z=7{xu+F_b4KrM}^ z7w6cPzHOm-r0rd_SjLt}g@!NLw7Y^~J8tCV81&oi4jd21={j6xi!2!G%;hQ>nrrtk zyyY(wSi7JIsL!?Fo|%kMG)F5_KJWQ_Vt-uR1T7f&S|>jez?o}!qGiV^*CTq8xXS&L z%i;P<1!j@Twr}3MIqbd2=k_cD(Q8AzF+87Yed!JP^W>B2PYY>! z+k=nF*n95jlyH6rI88bw+;sjFR3vB5&b|~@CR%jgk&u?Tabc$sW z+s03|SsX(46J1hbhW&i1HpC+DlA`;)KkST>o2%S}|019TJlQ<#W!HpD*%Z}q{YoDx zbcA0;!+^UEhr9XcPt^8jNm50rG;31H zropwtw2AMsvOGuRaG|JA;%s=iQ4GfZK{Y0Ss5Up_?a4pH*po7Zx&|idv=MDN-;CJ; z562XbkL5(&dR&j-{Pg>2tg1vP)kUX6^5PrCWW15wML(4dQxr9nc14j$wwjiq^!>CK zodkY*;l?y-p|M8BMiOa=Q!$GbcIIX(v! zHAaLMISNdh+Xub=!Y7KT`fcg8nTz7yjO9B1DjFBR!7yL%!9ZkqJM~qSWTjW*??(;n zF0}5dolo(}ylTp88`0suSFpxDJV#A6e0Xm0@cWzIjgCQoywMW6Bq#>{rvglkEDZC1 Jbq)o|e*xHM<_Q1* literal 0 HcmV?d00001 diff --git a/test/models/glTF2/BoxTextured-glTF-Embedded/BoxTextured.gltf b/test/models/glTF2/BoxTextured-glTF-Embedded/BoxTextured.gltf new file mode 100644 index 000000000..c9c461495 --- /dev/null +++ b/test/models/glTF2/BoxTextured-glTF-Embedded/BoxTextured.gltf @@ -0,0 +1,181 @@ +{ + "asset": { + "generator": "COLLADA2GLTF", + "version": "2.0" + }, + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "nodes": [ + { + "children": [ + 1 + ], + "matrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -1.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "mesh": 0 + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "NORMAL": 1, + "POSITION": 2, + "TEXCOORD_0": 3 + }, + "indices": 0, + "mode": 4, + "material": 0 + } + ], + "name": "Mesh" + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5123, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1.0, + 1.0, + 1.0 + ], + "min": [ + -1.0, + -1.0, + -1.0 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 288, + "componentType": 5126, + "count": 24, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 6.0, + 1.0 + ], + "min": [ + 0.0, + 0.0 + ], + "type": "VEC2" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0 + }, + "metallicFactor": 0.0 + }, + "name": "Texture" + } + ], + "textures": [ + { + "sampler": 0, + "source": 0 + } + ], + "images": [ + { + "uri": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANMAAADTCAMAAAAs2dbrAAAAsVBMVEXc3Nz8/f3+//9chyf///+VsHP4+/thiy3Z29dyl0R8nlH09vJeiCqzxZ6ft4GLqWatwZXw9vyEpFynvYxmjjTS3cNrkjvu8urn8vre7fjY6ffL08K6yqbEz7XT183K4vTQ5fW62fC/zq3J17fn7d/g6NbC3fLb5c/l5eVsrd+wzOF/t+OWw+WGu+TE099vr+CmyON5tOJ0seG50OHL1t7S2d7Z29yOv+Wdx+emzuyx1O6AHuoWAAAIi0lEQVR4XuzUS3LEIAxF0WznIYk/xun9LyxDRqnqGANq4rODW4L39d88Ho/H43HkZPzJVYgoxhiIpDJ7k2z5wJpizSkBv4vCJn1OWTZMeE9gY/Xfx3DE30Q2WfGBvOAa8lZnEKFH0JZ1GEE/MnpGw54R93Cs41ip4k6SjuWPjnC3YJZWtaJdqhJhlJDWLINgJJm/FoUxGpfJ0xAxnvMTv1UWzEF21pG8wzT+2OZIjeTxScZhLmdGz13FfHXoAGbCCmHgVCSHNVwaleSxjh8z4Yxmj09VBGtRGbAOq1G+OSlgvZC3SwLijVE2Qodot7lS4+xGSS1quyS4O/5UIegSSm/SUaENHZ1NDH1qX5SHRmdPUoJOqWPyInS6Pn6HoNlkJxh68T6fqTFXkkrERPL9en0L3ueufKkf6uxmt1EYiAN4sP7i0pNPABeItEIdeV0vJMTA+z/YaqWo3WSS1gn2lP4ewFHEfNqexLi5hAIAvTgKZZ+4myQxpsSZUtomiD75yPM5Pqg8uDK5cbORZzT+p3JDgfxma16LK3WaceLUkxQPJjj6hnGbo2sHpkkxzB4didFgcgp23OJQ5BS4Pn6TeiM5PThYCva2vQmCHDgYCmZXfCbJfMpc9PXQk6QGTE0PMNv7TDSBmaM/IXqSVeKKdsSsy6gjCfMZLijWSVaXvomkzbiwRF/jR0fS3JThXTbTw8aAVVCcbXFW2wRv8oa+hVmaumoWS88YTgKFXNpBYHrd1GXfaaCfyI0CK7u0g0DoSfMCoSduTBF6Zto3bV033ewdiel/L/9+te3G6HOR35d4p/JmcjI9rVI4e9nd0z9XSStc08tAidkmw4cs6khuK3BK6ZlS6huFC6/xZj23z3BHbSiZOceVIto2aCrcp1N1BteByXe3DfQgq/EZNVMKQwUui5ROPscXFoqvL3HLa5R0sjkg/6eGEjcVMQajXuNraqK4XA3ufoey9AhXIURmUzyDhCbUya0/nFN6SHIbyK0vERMCqZZW4ikcXCQOcQ7nlgT1gVPFjvuz+vCXX4UCl/mEd+tAXhSsSDxT9rq/tJjbasJAFEWJm2QwF6NJVNra0hptoFVsERrp/39YXwTrbDWZOcf1GrLDhrMyF45elUMAVTjlR5NvrctNIsniFDB5HVzstJWEB+MUR/I5PdVRahtQcF3hSNxEkoMGhw+SAidmU1bqLjIFI5wwS670IwhPYvzHNCRweweZBiHOeBH8yik8KnBOtWal9GUqcc4T38D6h49gM0zoNLXTlqmGBQ/fXhDOhKyUskyrChYZ78ol4cyCpn+jKlMUw6bkTpJwxjgqJZ93jHgbIQt3Ukou0zOYkDv5rUw1rpDmtE9685VpYn91ZXp1OjiFs0xMpqXUa795z7mTRnjH0jvxuh77pXkP0a/TRh7erdSDh1ItybTERQruJA5nNJTia4+16dvp4C4Th+srxfOeDKHVaccyUTiRNnan6FNNpm6fvhTCmVlil3JTqh3wvDt1ksvE5PTeu5ZM3evTh0o4MxZcY+4eO2QSdeobzpgpK6UjU/d+b68TzszmvqvUhmRa4Aal05mwdQhnClbKU6bG4AYZn91dwv94t+MepYEgCuDTzhSudIFSgKIHKtwlaIBEe5cTv/8XM8ZEIrPtGxzw9zd5TcO83SbbgjKhSsnJXyZtRZfeHOFAmKut7fgF2siFdMyd1qQ0/nB7pZbN9WUacrcPpOwd4bev1B6USeuTdnA0FZqCF67h6Rae95y0r45wTO9SR0+ZtFCR9uIIx2agUv55H5H26gnHqp7906395W/TXWBkGD/7xOGiw/2VwvNeLxhaU8Qeh6eFCrd7ugxLjsbTrWTM2IPtTbeNIdxu9myr1EnNe8lYThFvL65wLAyilcLzPg+MZRTz6gzHRoaTgWYZKxNWUlRzZbi/UpKc4MNlUrHFI0Udrg73Vyo5oKP10pZseCexeR8J9ztXKvrNfvNR/nHeK2px3jC+T2zh/kql6ebPxnhcCph3VCett/zx7dfYHT6lINxTKWWy2aurqnnHdYqrRdLJu2UiAsId+s8S0XJVmbJNTm0+iwLCPZXC0jkbZdQqkRYq3CETq3rGRltqVUhUqsI9wlYAdbqFzB4IDp8O99Ln19iIHaMHhi/N+KbCohaDFZutqUPhWB9864RnP1yQAoav5NurCrjk9dlsRJ16KnzK9zAAN/XUZ7tH6nQ56cWY7yPvWiiSku3CgAD5yy7newmrRFrUFV9jSEAhZ8Uo8P2EQfyvSoYzVtDmZFwlihJku4VM31Vvm/N1pgQVv6PnWZ//g2pby1m6my5+cls3ubHCQBCASypZbiPbIGDYAF7wXpScYO5/tCxZBgb8g78btKpdbZ6kA/708/Vah35kKrbxy+v7/9frX9dOPE/hb2KYi+V5dsYBjk+icIjhc+gZhyz1xQRp+BQ6ALUF5XFYz2eYBIfNmo/gcILnEzQ4QwzLZ2ecstVTELuWpTOCk8LEwq04batm83Zvz5I1gg+IeczftY7L2+FDHUul8DFVzWPaSc8SjTMuCIblsRt2lfSEw0WLZVmsx2Wu5Mqro9FbwR0GlqMX3OLtyztM16n6RgJ8PYu36+qph52zzEwJ7rZoZjUggnViPtohitAwl2lFLIp5NAHxdDZTO8S0GqY2OkQmqp69220T07EDkggtU2lmpOLSRKUHJCTeMro2IK25Z1xmRXqLYTxjJ8jh7QzjGAdBLhJlKu0DcpLO8F7TEJDd1lrexjhBCd7Bj7yDVivKIe56WL/t2jEKwCAMheEsYirEBgNdohl6CO9/tM6FgrRdFPJNWX/I+BIbTMao/MhKYjAlo/3LE8bCChND5RLfLXxPhNl1wMYhxXHOtktDWInWHMr2mHakkEkRVoXaaiUWyVmEqTY1hOX1+z3mnHPOXX/B0DoXjBYdAAAAAElFTkSuQmCC" + } + ], + "samplers": [ + { + "magFilter": 9729, + "minFilter": 9986, + "wrapS": 10497, + "wrapT": 10497 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 768, + "byteLength": 72, + "target": 34963 + }, + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 576, + "byteStride": 12, + "target": 34962 + }, + { + "buffer": 0, + "byteOffset": 576, + "byteLength": 192, + "byteStride": 8, + "target": 34962 + } + ], + "buffers": [ + { + "byteLength": 840, + "uri": "data:application/octet-stream;base64,AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAvwAAAL8AAAA/AAAAPwAAAL8AAAA/AAAAvwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAL8AAAA/AAAAPwAAAD8AAAC/AAAAPwAAAL8AAAC/AAAAvwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAvwAAAD8AAAC/AAAAPwAAAD8AAAC/AAAAPwAAAL8AAAA/AAAAvwAAAL8AAAA/AAAAPwAAAL8AAAC/AAAAvwAAAL8AAAC/AAAAvwAAAL8AAAA/AAAAvwAAAD8AAAA/AAAAvwAAAL8AAAC/AAAAvwAAAD8AAAC/AAAAvwAAAL8AAAC/AAAAvwAAAD8AAAC/AAAAPwAAAL8AAAC/AAAAPwAAAD8AAAC/AADAQAAAAAAAAKBAAAAAAAAAwED+/38/AACgQP7/fz8AAIBAAAAAAAAAoEAAAAAAAACAQAAAgD8AAKBAAACAPwAAAEAAAAAAAACAPwAAAAAAAABAAACAPwAAgD8AAIA/AABAQAAAAAAAAIBAAAAAAAAAQEAAAIA/AACAQAAAgD8AAEBAAAAAAAAAAEAAAAAAAABAQAAAgD8AAABAAACAPwAAAAAAAAAAAAAAAP7/fz8AAIA/AAAAAAAAgD/+/38/AAABAAIAAwACAAEABAAFAAYABwAGAAUACAAJAAoACwAKAAkADAANAA4ADwAOAA0AEAARABIAEwASABEAFAAVABYAFwAWABUA" + } + ] +} diff --git a/test/models/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf b/test/models/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf new file mode 100644 index 000000000..b286239fa --- /dev/null +++ b/test/models/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf @@ -0,0 +1,197 @@ +{ + "asset": { + "generator": "COLLADA2GLTF", + "version": "2.0" + }, + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "nodes": [ + { + "children": [ + 1 + ], + "matrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -1.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "mesh": 0 + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "NORMAL": 1, + "POSITION": 2, + "TEXCOORD_0": 3 + }, + "indices": 0, + "mode": 4, + "material": 0 + } + ], + "name": "Mesh" + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5123, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1.0, + 1.0, + 1.0 + ], + "min": [ + -1.0, + -1.0, + -1.0 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 288, + "componentType": 5126, + "count": 24, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 6.0, + 1.0 + ], + "min": [ + 0.0, + 0.0 + ], + "type": "VEC2" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0 + }, + "metallicFactor": 0.0 + }, + "extensions": { + "KHR_materials_pbrSpecularGlossiness": { + "diffuseTexture": { + "index": 0 + }, + "specularFactor": [ + 0.20000000298023225, + 0.20000000298023225, + 0.20000000298023225 + ], + "glossinessFactor": 1.0 + } + }, + "name": "Texture" + } + ], + "textures": [ + { + "sampler": 0, + "source": 0 + } + ], + "images": [ + { + "uri": "CesiumLogoFlat.png" + } + ], + "samplers": [ + { + "magFilter": 9729, + "minFilter": 9986, + "wrapS": 10497, + "wrapT": 10497 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 768, + "byteLength": 72, + "target": 34963 + }, + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 576, + "byteStride": 12, + "target": 34962 + }, + { + "buffer": 0, + "byteOffset": 576, + "byteLength": 192, + "byteStride": 8, + "target": 34962 + } + ], + "buffers": [ + { + "byteLength": 840, + "uri": "BoxTextured0.bin" + } + ], + "extensionsUsed": [ + "KHR_materials_pbrSpecularGlossiness" + ] +} diff --git a/test/models/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured0.bin b/test/models/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured0.bin new file mode 100644 index 0000000000000000000000000000000000000000..d2a73551f9456732c7ca85b9d70895f5d9ab8171 GIT binary patch literal 840 zcma)4TMmLS5FAugR8)K(r@fgRg`3F9)Sc2X4N+i|$-K6U9|D@%NdZH8sMCdXhgs?; z>8CE)+Yvq1hwmphbb0bSz9n3Qv{?B+?(fNy1()2HW=AbfwKB4_dS!i9PnJ%1er4>H zi~S!ZIHJM{XG4VxuDIcDxZ(qtxm=$B literal 0 HcmV?d00001 diff --git a/test/models/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/CesiumLogoFlat.png b/test/models/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/CesiumLogoFlat.png new file mode 100644 index 0000000000000000000000000000000000000000..45d502ed2610974189307ad542b05b70df14abd4 GIT binary patch literal 2433 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 literal 0 HcmV?d00001 diff --git a/test/models/glTF2/BoxTextured-glTF-techniqueWebGL/BoxTextured0.vert b/test/models/glTF2/BoxTextured-glTF-techniqueWebGL/BoxTextured0.vert new file mode 100644 index 000000000..05d5e82c2 --- /dev/null +++ b/test/models/glTF2/BoxTextured-glTF-techniqueWebGL/BoxTextured0.vert @@ -0,0 +1,17 @@ +precision highp float; +uniform mat4 u_modelViewMatrix; +uniform mat4 u_projectionMatrix; +uniform mat3 u_normalMatrix; +attribute vec3 a_position; +varying vec3 v_position; +attribute vec3 a_normal; +varying vec3 v_normal; +attribute vec2 a_texcoord0; +varying vec2 v_texcoord0; +void main(void) { + vec4 pos = u_modelViewMatrix * vec4(a_position,1.0); + v_position = pos.xyz; + gl_Position = u_projectionMatrix * pos; + v_normal = u_normalMatrix * a_normal; + v_texcoord0 = a_texcoord0; +} diff --git a/test/models/glTF2/BoxTextured-glTF-techniqueWebGL/BoxTextured1.frag b/test/models/glTF2/BoxTextured-glTF-techniqueWebGL/BoxTextured1.frag new file mode 100644 index 000000000..3850980a0 --- /dev/null +++ b/test/models/glTF2/BoxTextured-glTF-techniqueWebGL/BoxTextured1.frag @@ -0,0 +1,29 @@ +precision highp float; +uniform sampler2D u_diffuse; +uniform vec4 u_specular; +uniform float u_shininess; +uniform float u_transparency; +varying vec3 v_position; +varying vec3 v_normal; +varying vec2 v_texcoord0; +void main(void) { + vec3 normal = normalize(v_normal); + vec4 diffuse = texture2D(u_diffuse, v_texcoord0); + vec3 diffuseLight = vec3(0.0, 0.0, 0.0); + vec3 specular = u_specular.rgb; + vec3 specularLight = vec3(0.0, 0.0, 0.0); + vec3 ambient = diffuse.rgb; + vec3 viewDir = -normalize(v_position); + vec3 ambientLight = vec3(0.0, 0.0, 0.0); + ambientLight += vec3(0.2, 0.2, 0.2); + vec3 l = vec3(0.0, 0.0, 1.0); + diffuseLight += vec3(1.0, 1.0, 1.0) * max(dot(normal, l), 0.); + vec3 reflectDir = reflect(-l, normal); + float specularIntensity = max(0., pow(max(dot(reflectDir, viewDir), 0.), u_shininess)); + specularLight += vec3(1.0, 1.0, 1.0) * specularIntensity; + vec3 color = vec3(0.0, 0.0, 0.0); + color += diffuse.rgb * diffuseLight; + color += specular * specularLight; + color += ambient * ambientLight; + gl_FragColor = vec4(color * diffuse.a * u_transparency, diffuse.a * u_transparency); +} diff --git a/test/models/glTF2/BoxTextured-glTF-techniqueWebGL/CesiumLogoFlat.png b/test/models/glTF2/BoxTextured-glTF-techniqueWebGL/CesiumLogoFlat.png new file mode 100644 index 0000000000000000000000000000000000000000..45d502ed2610974189307ad542b05b70df14abd4 GIT binary patch literal 2433 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 literal 0 HcmV?d00001 diff --git a/test/models/glTF2/BoxTextured-glTF/CesiumLogoFlat.png b/test/models/glTF2/BoxTextured-glTF/CesiumLogoFlat.png new file mode 100644 index 0000000000000000000000000000000000000000..45d502ed2610974189307ad542b05b70df14abd4 GIT binary patch literal 2433 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 Date: Wed, 30 Aug 2017 17:25:11 -0400 Subject: [PATCH 023/490] Working read, import, export, and write of gltf2 (pbr) material --- code/glTF2Asset.h | 51 ++++++++------ code/glTF2Asset.inl | 115 +++++++++++++++++++----------- code/glTF2AssetWriter.inl | 145 ++++++++++++++++++++++++++++++++------ code/glTF2Exporter.cpp | 50 +++++++------ code/glTF2Exporter.h | 10 ++- code/glTF2Importer.cpp | 93 +++++++++++++++++------- 6 files changed, 329 insertions(+), 135 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 5d99735b2..509fef115 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -133,7 +133,6 @@ namespace glTF2 struct Light; struct Skin; - // Vec/matrix types, as raw float arrays typedef float (vec3)[3]; typedef float (vec4)[4]; @@ -671,20 +670,30 @@ namespace glTF2 inline void SetData(uint8_t* data, size_t length, Asset& r); }; - struct ColorProperty - { - union { - vec4, vec3 - } color; - } - - //! Holds a material property that can be a texture or a color (fallback for glTF 1) - struct FallbackTexProperty + struct TextureInfo { Ref texture; - ColorProperty color; + unsigned int index; + unsigned int texCoord = 0; }; + struct NormalTextureInfo : TextureInfo + { + float scale = 1; + }; + + struct OcclusionTextureInfo : TextureInfo + { + float strength = 1; + }; + + //! Holds a material property that can be a texture or a color (fallback for glTF 1) + /*struct FallbackTexProperty + { + Ref texture; + vec4 color; + };*/ + //! The material appearance of a primitive. struct Material : public Object { @@ -702,26 +711,26 @@ namespace glTF2 }; //PBR metallic roughness properties - ColorProperty baseColor; - Ref baseColorTexture; - Ref metallicRoughnessTexture; + vec4 baseColorFactor; + TextureInfo baseColorTexture; + TextureInfo metallicRoughnessTexture; float metallicFactor; float roughnessFactor; //other basic material properties - Ref normalTexture; - Ref occlusionTexture; - Ref emissiveTexture; - ColorProperty emissiveFactor; + NormalTextureInfo normalTexture; + OcclusionTextureInfo occlusionTexture; + TextureInfo emissiveTexture; + vec3 emissiveFactor; std::string alphaMode; float alphaCutoff; bool doubleSided; //fallback material properties (compatible with non-pbr defintions) - FallbackTexProperty diffuse; - FallbackTexProperty emissive; + /*FallbackTexProperty diffuse; + FallbackTexProperty emission; FallbackTexProperty specular; - Ref normal; + Ref normal;*/ Technique technique; diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index bf8cb5a36..94858044d 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -128,6 +128,12 @@ namespace { return (it != val.MemberEnd() && it->value.IsString()) ? &it->value : 0; } + inline Value* FindNumber(Value& val, const char* id) + { + Value::MemberIterator it = val.FindMember(id); + return (it != val.MemberEnd() && it->value.IsNumber()) ? &it->value : 0; + } + inline Value* FindUInt(Value& val, const char* id) { Value::MemberIterator it = val.FindMember(id); @@ -225,7 +231,7 @@ Ref LazyDict::Retrieve(unsigned int i) } T* inst = new T(); - inst->id = std::string(mDictId) + "[" + std::to_string(i) + "]"; + inst->id = std::string(mDictId) + "_" + std::to_string(i); inst->oIndex = i; ReadMember(obj, "name", inst->name); inst->Read(obj, mAsset); @@ -706,15 +712,42 @@ inline void Texture::Read(Value& obj, Asset& r) } namespace { - inline void ReadMaterialProperty(Asset& r, Value& vals, const char* propName, TexProperty& out) + inline void SetTextureProperties(Asset& r, Value* prop, TextureInfo& out) + { + if (Value* index = FindUInt(*prop, "index")) { + out.texture = r.textures.Retrieve(index->GetUint()); + } + + if (Value* texcoord = FindUInt(*prop, "texCoord")) { + out.texCoord = texcoord->GetUint(); + } + } + + inline void ReadTextureProperty(Asset& r, Value& vals, const char* propName, TextureInfo& out) { - //@TODO: update this format if (Value* prop = FindMember(vals, propName)) { - if (prop->IsUint()) { - out.texture = r.textures.Retrieve(prop->GetUint()); + SetTextureProperties(r, prop, out); + } + } + + inline void ReadTextureProperty(Asset& r, Value& vals, const char* propName, NormalTextureInfo& out) + { + if (Value* prop = FindMember(vals, propName)) { + SetTextureProperties(r, prop, out); + + if (Value* scale = FindNumber(*prop, "scale")) { + out.scale = scale->GetDouble(); } - else { - ReadValue(*prop, out.color); + } + } + + inline void ReadTextureProperty(Asset& r, Value& vals, const char* propName, OcclusionTextureInfo& out) + { + if (Value* prop = FindMember(vals, propName)) { + SetTextureProperties(r, prop, out); + + if (Value* strength = FindNumber(*prop, "strength")) { + out.strength = strength->GetDouble(); } } } @@ -724,33 +757,24 @@ inline void Material::Read(Value& material, Asset& r) { SetDefaults(); - if (Value* values = FindObject(material, "values")) { - - - ReadMember(*values, "transparency", transparency); - } - if (Value* values = FindObject(material, "pbrMetallicRoughness")) { - //pbr - ReadMaterialProperty(r, *values, "baseColorFactor", this->baseColor); - ReadMaterialProperty(r, *values, "baseColorTexture", this->baseColorTexture); - - //non-pbr fallback - ReadMaterialProperty(r, *values, "baseColorFactor", this->diffuse); - ReadMaterialProperty(r, *values, "baseColorTexture", this->diffuse); - - ReadMember(*values, "metallicFactor", metallicFactor); + ReadMember(*values, "baseColorFactor", this->baseColorFactor); + ReadTextureProperty(r, *values, "baseColorTexture", this->baseColorTexture); + ReadTextureProperty(r, *values, "metallicRoughnessTexture", this->metallicRoughnessTexture); + ReadMember(*values, "metallicFactor", this->metallicFactor); + ReadMember(*values, "roughnessFactor", this->roughnessFactor); } - ReadMaterialProperty(r, *values, "normalTexture", this->normalTexture); - ReadMaterialProperty(r, *values, "normalTexture", this->normal); - ReadMaterialProperty(r, *values, "occlusionTexture", this->occlusionTexture); - ReadMaterialProperty(r, *values, "emissiveTexture", this->emissiveTexture); - ReadMember(*values, "metallicFactor", emissiveFactor); + ReadTextureProperty(r, material, "normalTexture", this->normalTexture); + ReadTextureProperty(r, material, "occlusionTexture", this->occlusionTexture); + ReadTextureProperty(r, material, "emissiveTexture", this->emissiveTexture); + ReadMember(material, "emissiveFactor", this->emissiveFactor); - ReadMember(material, "doubleSided", doubleSided); + ReadMember(material, "doubleSided", this->doubleSided); + ReadMember(material, "alphaMode", this->alphaMode); + ReadMember(material, "alphaCutoff", this->alphaCutoff); - if (Value* extensions = FindObject(material, "extensions")) { + /* if (Value* extensions = FindObject(material, "extensions")) { if (r.extensionsUsed.KHR_materials_common) { if (Value* ext = FindObject(*extensions, "KHR_materials_common")) { if (Value* tnq = FindString(*ext, "technique")) { @@ -762,9 +786,9 @@ inline void Material::Read(Value& material, Asset& r) } if (Value* values = FindObject(*ext, "values")) { - ReadMaterialProperty(r, *values, "ambient", this->ambient); - ReadMaterialProperty(r, *values, "diffuse", this->diffuse); - ReadMaterialProperty(r, *values, "specular", this->specular); + ReadTextureProperty(r, *values, "ambient", this->ambient); + ReadTextureProperty(r, *values, "diffuse", this->diffuse); + ReadTextureProperty(r, *values, "specular", this->specular); ReadMember(*values, "doubleSided", doubleSided); ReadMember(*values, "transparent", transparent); @@ -773,25 +797,28 @@ inline void Material::Read(Value& material, Asset& r) } } } - } + } */ } namespace { void SetVector(vec4& v, float x, float y, float z, float w) { v[0] = x; v[1] = y; v[2] = z; v[3] = w; } + + void SetVector(vec3& v, float x, float y, float z) + { v[0] = x; v[1] = y; v[2] = z; } } inline void Material::SetDefaults() { - SetVector(ambient.color, 0, 0, 0, 1); - SetVector(diffuse.color, 0, 0, 0, 1); - SetVector(specular.color, 0, 0, 0, 1); - SetVector(emission.color, 0, 0, 0, 1); + //pbr materials + SetVector(baseColorFactor, 1, 1, 1, 1); + SetVector(emissiveFactor, 0, 0, 0); + metallicFactor = 1.0; + roughnessFactor = 1.0; + alphaMode = "OPAQUE"; + alphaCutoff = 0.5; doubleSided = false; - transparent = false; - transparency = 1.0; - shininess = 0.0; technique = Technique_undefined; } @@ -833,6 +860,10 @@ namespace { inline void Mesh::Read(Value& pJSON_Object, Asset& pAsset_Root) { + if (Value* name = FindMember(pJSON_Object, "name")) { + this->name = name->GetString(); + } + /****************** Mesh primitives ******************/ if (Value* primitives = FindArray(pJSON_Object, "primitives")) { this->primitives.resize(primitives->Size()); @@ -1055,7 +1086,7 @@ inline void Node::Read(Value& obj, Asset& r) // TODO load "skeletons", "skin", "jointName" - if (Value* extensions = FindObject(obj, "extensions")) { + /*if (Value* extensions = FindObject(obj, "extensions")) { if (r.extensionsUsed.KHR_materials_common) { if (Value* ext = FindObject(*extensions, "KHR_materials_common")) { @@ -1065,7 +1096,7 @@ inline void Node::Read(Value& obj, Asset& r) } } - } + }*/ } inline void Scene::Read(Value& obj, Asset& r) diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index ed8ee80ea..a939e38d6 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -72,6 +72,12 @@ namespace glTF2 { return val; } + inline Value& MakeValue(Value& val, float r, MemoryPoolAllocator<>& al) { + val.SetDouble(r); + + return val; + } + template inline void AddRefsVector(Value& obj, const char* fieldId, std::vector< Ref >& v, MemoryPoolAllocator<>& al) { if (v.empty()) return; @@ -196,51 +202,148 @@ namespace glTF2 { } namespace { - inline void WriteTex(Value& obj, Ref texture, const char* propName, MemoryPoolAllocator<>& al) + inline void SetTexBasic(TextureInfo t, Value& tex, MemoryPoolAllocator<>& al) { - if (texture) { + tex.SetObject(); + tex.AddMember("index", t.texture->index, al); + + if (t.texCoord != 0) { + tex.AddMember("texCoord", t.texCoord, al); + } + } + + inline void WriteTex(Value& obj, TextureInfo t, const char* propName, MemoryPoolAllocator<>& al) + { + + if (t.texture) { Value tex; - tex.SetObject(); - tex.AddMember("index", texture->index, al); + + SetTexBasic(t, tex, al); + obj.AddMember(StringRef(propName), tex, al); } } - inline void WriteColorOrTex(Value& obj, TexProperty& prop, const char* propName, MemoryPoolAllocator<>& al) + inline void WriteTex(Value& obj, NormalTextureInfo t, const char* propName, MemoryPoolAllocator<>& al) { - WriteTex(obj, prop.texture, propName, al); - if (!prop.texture) { - Value col; - obj.AddMember(StringRef(propName), MakeValue(col, prop.color, al), al); + + if (t.texture) { + Value tex; + + SetTexBasic(t, tex, al); + + if (t.scale != 1) { + tex.AddMember("scale", t.scale, al); + } + + obj.AddMember(StringRef(propName), tex, al); } } + + inline void WriteTex(Value& obj, OcclusionTextureInfo t, const char* propName, MemoryPoolAllocator<>& al) + { + + if (t.texture) { + Value tex; + + SetTexBasic(t, tex, al); + + if (t.strength != 1) { + tex.AddMember("strength", t.strength, al); + } + + obj.AddMember(StringRef(propName), tex, al); + } + } + + template + inline void WriteVec(Value& obj, float(&prop)[N], const char* propName, MemoryPoolAllocator<>& al) + { + Value arr; + obj.AddMember(StringRef(propName), MakeValue(arr, prop, al), al); + } + + template + inline void WriteVec(Value& obj, float(&prop)[N], const char* propName, float(&defaultVal)[N], MemoryPoolAllocator<>& al) + { + if (!std::equal(std::begin(prop), std::end(prop), std::begin(defaultVal))) { + WriteVec(obj, prop, propName, al); + } + } + + inline void WriteFloat(Value& obj, float prop, const char* propName, MemoryPoolAllocator<>& al) + { + Value num; + obj.AddMember(StringRef(propName), MakeValue(num, prop, al), al); + } } inline void Write(Value& obj, Material& m, AssetWriter& w) { - if (m.transparent) { - obj.AddMember("alphaMode", "BLEND", w.mAl); + if (!m.name.empty()) { + obj.AddMember("name", m.name, w.mAl); } - Value v; + Value pbrMetallicRoughness; + pbrMetallicRoughness.SetObject(); + { + WriteTex(pbrMetallicRoughness, m.baseColorTexture, "baseColorTexture", w.mAl); + WriteTex(pbrMetallicRoughness, m.metallicRoughnessTexture, "metallicRoughnessTexture", w.mAl); + + //@TODO: define this as a constant? + vec4 defaultEmissiveFactor = {1, 1, 1, 1}; + WriteVec(pbrMetallicRoughness, m.baseColorFactor, "baseColorFactor", defaultEmissiveFactor, w.mAl); + + if (m.metallicFactor != 1) { + WriteFloat(pbrMetallicRoughness, m.metallicFactor, "metallicFactor", w.mAl); + } + + if (m.roughnessFactor != 1) { + WriteFloat(pbrMetallicRoughness, m.roughnessFactor, "roughnessFactor", w.mAl); + } + } + + if (pbrMetallicRoughness.MemberCount() > 0) { + obj.AddMember("pbrMetallicRoughness", pbrMetallicRoughness, w.mAl); + } + + WriteTex(obj, m.normalTexture, "normalTexture", w.mAl); + WriteTex(obj, m.emissiveTexture, "emissiveTexture", w.mAl); + WriteTex(obj, m.occlusionTexture, "occlusionTexture", w.mAl); + + //@TODO: define this as a constant? + vec3 defaultEmissiveFactor = {0, 0, 0}; + WriteVec(obj, m.emissiveFactor, "emissiveFactor", defaultEmissiveFactor, w.mAl); + + if (m.alphaCutoff != 0.5) { + WriteFloat(obj, m.alphaCutoff, "alphaCutoff", w.mAl); + } + + if (m.alphaMode != "OPAQUE") { + obj.AddMember("alphaMode", m.alphaMode, w.mAl); + } + + if (m.doubleSided) { + obj.AddMember("doubleSided", m.doubleSided, w.mAl); + } + + /*Value v; v.SetObject(); { if (m.transparent && !m.diffuse.texture) { m.diffuse.color[3] = m.transparency; } - WriteColorOrTex(v, m.ambient, m.ambient.texture ? "ambientTexture" : "ambientFactor", w.mAl); - WriteColorOrTex(v, m.diffuse, m.diffuse.texture ? "diffuseTexture" : "diffuseFactor", w.mAl); - WriteColorOrTex(v, m.specular, m.specular.texture ? "specularTexture" : "specularFactor", w.mAl); - WriteColorOrTex(v, m.emission, m.emission.texture ? "emissionTexture" : "emissionFactor", w.mAl); + WriteVecOrTex(v, m.ambient, m.ambient.texture ? "ambientTexture" : "ambientFactor", w.mAl); + WriteVecOrTex(v, m.diffuse, m.diffuse.texture ? "diffuseTexture" : "diffuseFactor", w.mAl); + WriteVecOrTex(v, m.specular, m.specular.texture ? "specularTexture" : "specularFactor", w.mAl); + WriteVecOrTex(v, m.emission, m.emission.texture ? "emissionTexture" : "emissionFactor", w.mAl); v.AddMember("shininessFactor", m.shininess, w.mAl); } v.AddMember("type", "commonPhong", w.mAl); Value ext; ext.SetObject(); ext.AddMember("KHR_materials_common", v, w.mAl); - obj.AddMember("extensions", ext, w.mAl); - - WriteTex(obj, m.normal, "normalTexture", w.mAl); + obj.AddMember("extensions", ext, w.mAl);*/ } namespace { @@ -263,8 +366,6 @@ namespace glTF2 { inline void Write(Value& obj, Mesh& m, AssetWriter& w) { - /********************* Name **********************/ - obj.AddMember("name", m.name, w.mAl); /**************** Mesh extensions ****************/ if(m.Extension.size() > 0) @@ -617,7 +718,7 @@ namespace glTF2 { exts.PushBack(StringRef("KHR_binary_glTF"), mAl); // This is used to export common materials with GLTF 2. - exts.PushBack(StringRef("KHR_materials_common"), mAl); + //exts.PushBack(StringRef("KHR_materials_common"), mAl); } if (!exts.Empty()) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 68f2f4420..f59c280f1 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -234,12 +234,6 @@ inline Ref ExportData(Asset& a, std::string& meshName, Ref& bu return acc; } -namespace { - void GetMatScalar(const aiMaterial* mat, float& val, const char* propName, int type, int idx) { - if (mat->Get(propName, type, idx, val) == AI_SUCCESS) {} - } -} - void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture) { std::string samplerId = mAsset->FindUniqueID("", "sampler"); @@ -286,11 +280,11 @@ void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture) texture->sampler->minFilter = SamplerMinFilter_Linear_Mipmap_Linear; } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTextureType tt) +void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) { aiString tex; if (mat->GetTextureCount(tt) > 0) { - if (mat->Get(AI_MATKEY_TEXTURE(tt, 0), tex) == AI_SUCCESS) { + if (mat->Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) { std::string path = tex.C_Str(); if (path.size() > 0) { @@ -332,15 +326,21 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe } } -void glTF2Exporter::GetMatColorOrTex(const aiMaterial* mat, TexProperty& prop, const char* propName, int type, int idx, aiTextureType tt) +void glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const char* propName, int type, int idx) { aiColor4D col; if (mat->Get(propName, type, idx, col) == AI_SUCCESS) { - prop.color[0] = col.r; prop.color[1] = col.g; prop.color[2] = col.b; prop.color[3] = col.a; + prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; prop[3] = col.a; } - GetMatTex(mat, prop.texture, tt); } +void glTF2Exporter::GetMatColor(const aiMaterial* mat, vec3& prop, const char* propName, int type, int idx) +{ + aiColor3D col; + if (mat->Get(propName, type, idx, col) == AI_SUCCESS) { + prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; + } +} void glTF2Exporter::ExportMaterials() { @@ -348,7 +348,6 @@ void glTF2Exporter::ExportMaterials() for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) { const aiMaterial* mat = mScene->mMaterials[i]; - std::string name; if (mat->Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) { name = aiName.C_Str(); @@ -357,15 +356,20 @@ void glTF2Exporter::ExportMaterials() Ref m = mAsset->materials.Create(name); - GetMatColorOrTex(mat, m->ambient, AI_MATKEY_COLOR_AMBIENT, aiTextureType_AMBIENT); - GetMatColorOrTex(mat, m->diffuse, AI_MATKEY_COLOR_DIFFUSE, aiTextureType_DIFFUSE); - GetMatColorOrTex(mat, m->specular, AI_MATKEY_COLOR_SPECULAR, aiTextureType_SPECULAR); - GetMatColorOrTex(mat, m->emission, AI_MATKEY_COLOR_EMISSIVE, aiTextureType_EMISSIVE); - GetMatTex(mat, m->normal, aiTextureType_NORMALS); + GetMatTex(mat, m->baseColorTexture.texture, aiTextureType_DIFFUSE); + GetMatTex(mat, m->metallicRoughnessTexture.texture, aiTextureType_UNKNOWN, 0);//get unknown slot + GetMatTex(mat, m->emissiveTexture.texture, aiTextureType_EMISSIVE); + GetMatTex(mat, m->normalTexture.texture, aiTextureType_NORMALS); + GetMatTex(mat, m->occlusionTexture.texture, aiTextureType_LIGHTMAP); - m->transparent = mat->Get(AI_MATKEY_OPACITY, m->transparency) == aiReturn_SUCCESS && m->transparency != 1.0; + GetMatColor(mat, m->baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); + GetMatColor(mat, m->emissiveFactor, AI_MATKEY_COLOR_EMISSIVE); - GetMatScalar(mat, m->shininess, AI_MATKEY_SHININESS); + mat->Get(AI_MATKEY_TWOSIDED, m->doubleSided); + mat->Get("$mat.gltf.alphaCutoff", 0, 0, m->alphaCutoff); + mat->Get("$mat.gltf.metallicFactor", 0, 0, m->metallicFactor); + mat->Get("$mat.gltf.roughnessFactor", 0, 0, m->roughnessFactor); + mat->Get("$mat.gltf.alphaMode", 0, 0, m->alphaMode); } } @@ -566,13 +570,17 @@ void glTF2Exporter::ExportMeshes() DefaultLogger::get()->warn("GLTF: can not use Open3DGC-compression: " + msg); comp_allow = false; - } + } - std::string meshId = mAsset->FindUniqueID(aim->mName.C_Str(), "mesh"); + std::string name = aim->mName.C_Str(); + + std::string meshId = mAsset->FindUniqueID(name, "mesh"); Ref m = mAsset->meshes.Create(meshId); m->primitives.resize(1); Mesh::Primitive& p = m->primitives.back(); + m->name = name; + p.material = mAsset->materials.Get(aim->mMaterialIndex); /******************* Vertices ********************/ diff --git a/code/glTF2Exporter.h b/code/glTF2Exporter.h index 17cc9cfdc..2ba0143bf 100644 --- a/code/glTF2Exporter.h +++ b/code/glTF2Exporter.h @@ -66,8 +66,13 @@ namespace glTF2 class Asset; struct TexProperty; + struct TextureInfo; struct Node; struct Texture; + + // Vec/matrix types, as raw float arrays + typedef float (vec3)[3]; + typedef float (vec4)[4]; } namespace Assimp @@ -102,8 +107,9 @@ namespace Assimp void WriteBinaryData(IOStream* outfile, std::size_t sceneLength); void GetTexSampler(const aiMaterial* mat, glTF2::Ref texture); - void GetMatTex(const aiMaterial* mat, glTF2::Ref& texture, aiTextureType tt); - void GetMatColorOrTex(const aiMaterial* mat, glTF2::TexProperty& prop, const char* propName, int type, int idx, aiTextureType tt); + void GetMatTex(const aiMaterial* mat, glTF2::Ref& texture, aiTextureType tt, unsigned int slot); + void GetMatColor(const aiMaterial* mat, glTF2::vec4& prop, const char* propName, int type, int idx); + void GetMatColor(const aiMaterial* mat, glTF2::vec3& prop, const char* propName, int type, int idx); void ExportMetadata(); void ExportMaterials(); void ExportMeshes(); diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index bd4f42600..25cb39168 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -141,6 +141,11 @@ static void CopyValue(const glTF2::vec4& v, aiColor3D& out) out.r = v[0]; out.g = v[1]; out.b = v[2]; } +static void CopyValue(const glTF2::vec3& v, aiColor4D& out) +{ + out.r = v[0]; out.g = v[1]; out.b = v[2]; out.a = 1.0; +} + static void CopyValue(const glTF2::vec3& v, aiVector3D& out) { out.x = v[0]; out.y = v[1]; out.z = v[2]; @@ -159,28 +164,56 @@ static void CopyValue(const glTF2::mat4& v, aiMatrix4x4& o) o.a4 = v[12]; o.b4 = v[13]; o.c4 = v[14]; o.d4 = v[15]; } -inline void SetMaterialColorProperty(std::vector& embeddedTexIdxs, Asset& r, glTF2::TexProperty prop, aiMaterial* mat, - aiTextureType texType, const char* pKey, unsigned int type, unsigned int idx) +inline void SetMaterialColorProperty(Asset& r, vec4& prop, aiMaterial* mat, const char* pKey, unsigned int type, unsigned int idx) { - if (prop.texture) { - if (prop.texture->source) { - aiString uri(prop.texture->source->uri); + aiColor4D col; + CopyValue(prop, col); + mat->AddProperty(&col, 1, pKey, type, idx); +} - int texIdx = embeddedTexIdxs[prop.texture->source.GetIndex()]; - if (texIdx != -1) { // embedded - // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture) - uri.data[0] = '*'; - uri.length = 1 + ASSIMP_itoa10(uri.data + 1, MAXLEN - 1, texIdx); - } +inline void SetMaterialColorProperty(Asset& r, vec3& prop, aiMaterial* mat, const char* pKey, unsigned int type, unsigned int idx) +{ + vec4 prop4; + prop4[0] = prop[0]; + prop4[1] = prop[1]; + prop4[2] = prop[2]; + prop4[3] = 1; + + return SetMaterialColorProperty(r, prop4, mat, pKey, type, idx); +} + +inline void SetMaterialTextureProperty(std::vector& embeddedTexIdxs, Asset& r, glTF2::TextureInfo prop, aiMaterial* mat, aiTextureType texType, int texSlot) +{ + if (prop.texture && prop.texture->source) { + aiString uri(prop.texture->source->uri); + + int texIdx = embeddedTexIdxs[prop.texture->source.GetIndex()]; + if (texIdx != -1) { // embedded + // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture) + uri.data[0] = '*'; + uri.length = 1 + ASSIMP_itoa10(uri.data + 1, MAXLEN - 1, texIdx); + } + + if (texSlot < 0) { mat->AddProperty(&uri, _AI_MATKEY_TEXTURE_BASE, texType, 0); } + else { + mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, + texSlot)); + } } - else { - aiColor4D col; - CopyValue(prop.color, col); - mat->AddProperty(&col, 1, pKey, type, idx); - } +} + +//import textures that are only supported in pbr contexts +inline void SetMaterialPBRTextureProperty(std::vector& embeddedTexIdxs, Asset& r, glTF2::TextureInfo prop, aiMaterial* mat, unsigned int texSlot) +{ + return SetMaterialTextureProperty(embeddedTexIdxs, r, prop, mat, aiTextureType_UNKNOWN, texSlot); +} + +inline void SetMaterialTextureProperty(std::vector& embeddedTexIdxs, Asset& r, glTF2::TextureInfo prop, aiMaterial* mat, aiTextureType texType) +{ + return SetMaterialTextureProperty(embeddedTexIdxs, r, prop, mat, texType, -1); } void glTF2Importer::ImportMaterials(glTF2::Asset& r) @@ -193,18 +226,23 @@ void glTF2Importer::ImportMaterials(glTF2::Asset& r) Material& mat = r.materials[i]; - /*if (!mat.name.empty())*/ { - aiString str(mat.id /*mat.name*/); - aimat->AddProperty(&str, AI_MATKEY_NAME); - } + aiString str(mat.id); + aimat->AddProperty(&str, AI_MATKEY_NAME); - SetMaterialColorProperty(embeddedTexIdxs, r, mat.diffuse, aimat, aiTextureType_DIFFUSE, AI_MATKEY_COLOR_DIFFUSE); - SetMaterialColorProperty(embeddedTexIdxs, r, mat.specular, aimat, aiTextureType_SPECULAR, AI_MATKEY_COLOR_SPECULAR); - SetMaterialColorProperty(embeddedTexIdxs, r, mat.ambient, aimat, aiTextureType_AMBIENT, AI_MATKEY_COLOR_AMBIENT); + SetMaterialColorProperty(r, mat.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.baseColorTexture, aimat, aiTextureType_DIFFUSE); + SetMaterialPBRTextureProperty(embeddedTexIdxs, r, mat.metallicRoughnessTexture, aimat, 0); + aimat->AddProperty(&mat.metallicFactor, 1, "$mat.gltf.metallicFactor"); + aimat->AddProperty(&mat.roughnessFactor, 1, "$mat.gltf.roughnessFactor"); - if (mat.shininess > 0.f) { - aimat->AddProperty(&mat.shininess, 1, AI_MATKEY_SHININESS); - } + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.normalTexture, aimat, aiTextureType_NORMALS); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.occlusionTexture, aimat, aiTextureType_LIGHTMAP); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.emissiveTexture, aimat, aiTextureType_EMISSIVE); + SetMaterialColorProperty(r, mat.emissiveFactor, aimat, AI_MATKEY_COLOR_EMISSIVE); + + aimat->AddProperty(&mat.doubleSided, 1, AI_MATKEY_TWOSIDED); + aimat->AddProperty(&mat.alphaMode, 1, "$mat.gltf.alphaMode"); + aimat->AddProperty(&mat.alphaCutoff, 1, "$mat.gltf.alphaCutoff"); } } @@ -295,7 +333,8 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r) aiMesh* aim = new aiMesh(); meshes.push_back(aim); - aim->mName = mesh.id; + aim->mName = mesh.name.empty() ? mesh.id : mesh.name; + if (mesh.primitives.size() > 1) { size_t& len = aim->mName.length; aim->mName.data[len] = '-'; From 0cf69479c30c97a6f4bfeb00ffdceab5749b2b5f Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Wed, 30 Aug 2017 21:50:06 -0400 Subject: [PATCH 024/490] Use `!ObjectEmpty()` vs. `MemberCount() > 0` --- code/glTF2AssetWriter.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index a939e38d6..bd16a7023 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -303,7 +303,7 @@ namespace glTF2 { } } - if (pbrMetallicRoughness.MemberCount() > 0) { + if (!pbrMetallicRoughness.ObjectEmpty()) { obj.AddMember("pbrMetallicRoughness", pbrMetallicRoughness, w.mAl); } From 7615a97cd3164d624f3762dd3edf49f6e461a88d Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Wed, 30 Aug 2017 23:51:52 -0400 Subject: [PATCH 025/490] Remove redundant function --- code/glTF2Importer.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 25cb39168..b1bbf8349 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -205,12 +205,6 @@ inline void SetMaterialTextureProperty(std::vector& embeddedTexIdxs, Asset& } } -//import textures that are only supported in pbr contexts -inline void SetMaterialPBRTextureProperty(std::vector& embeddedTexIdxs, Asset& r, glTF2::TextureInfo prop, aiMaterial* mat, unsigned int texSlot) -{ - return SetMaterialTextureProperty(embeddedTexIdxs, r, prop, mat, aiTextureType_UNKNOWN, texSlot); -} - inline void SetMaterialTextureProperty(std::vector& embeddedTexIdxs, Asset& r, glTF2::TextureInfo prop, aiMaterial* mat, aiTextureType texType) { return SetMaterialTextureProperty(embeddedTexIdxs, r, prop, mat, texType, -1); @@ -231,7 +225,7 @@ void glTF2Importer::ImportMaterials(glTF2::Asset& r) SetMaterialColorProperty(r, mat.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.baseColorTexture, aimat, aiTextureType_DIFFUSE); - SetMaterialPBRTextureProperty(embeddedTexIdxs, r, mat.metallicRoughnessTexture, aimat, 0); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.metallicRoughnessTexture, aimat, aiTextureType_UNKNOWN, 0); aimat->AddProperty(&mat.metallicFactor, 1, "$mat.gltf.metallicFactor"); aimat->AddProperty(&mat.roughnessFactor, 1, "$mat.gltf.roughnessFactor"); From 863458cd4a30632350685f8aee88ed420118f577 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Thu, 31 Aug 2017 01:35:10 -0400 Subject: [PATCH 026/490] Start removing materials common, and adding pbrSpecularGlossiness --- code/glTF2Asset.h | 69 +++++---------------------------------- code/glTF2Asset.inl | 64 ++++++++++++------------------------ code/glTF2AssetWriter.h | 2 +- code/glTF2AssetWriter.inl | 42 +++++++++++++++--------- code/glTF2Importer.cpp | 7 ++++ 5 files changed, 63 insertions(+), 121 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 509fef115..b19de747a 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -44,7 +44,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * glTF Extensions Support: * KHR_binary_glTF: full - * KHR_materials_common: full */ #ifndef GLTF2ASSET_H_INC #define GLTF2ASSET_H_INC @@ -697,19 +696,6 @@ namespace glTF2 //! The material appearance of a primitive. struct Material : public Object { - //Ref source; //!< The ID of the technique. - //std::gltf_unordered_map values; //!< A dictionary object of parameter values. - - //! Techniques defined by KHR_materials_common - enum Technique - { - Technique_undefined = 0, - Technique_BLINN, - Technique_PHONG, - Technique_LAMBERT, - Technique_CONSTANT - }; - //PBR metallic roughness properties vec4 baseColorFactor; TextureInfo baseColorTexture; @@ -726,13 +712,12 @@ namespace glTF2 float alphaCutoff; bool doubleSided; - //fallback material properties (compatible with non-pbr defintions) - /*FallbackTexProperty diffuse; - FallbackTexProperty emission; - FallbackTexProperty specular; - Ref normal;*/ - - Technique technique; + //extension: KHR_materials_pbrSpecularGlossiness + vec4 diffuseFactor; + vec3 specularFactor; + float glossinessFactor; + TextureInfo diffuseTexture; + TextureInfo specularGlossinessTexture; Material() { SetDefaults(); } void Read(Value& obj, Asset& r); @@ -943,35 +928,6 @@ namespace glTF2 void Read(Value& obj, Asset& r); }; - - //! A light (from KHR_materials_common extension) - struct Light : public Object - { - enum Type - { - Type_undefined, - Type_ambient, - Type_directional, - Type_point, - Type_spot - }; - - Type type; - - vec4 color; - float distance; - float constantAttenuation; - float linearAttenuation; - float quadraticAttenuation; - float falloffAngle; - float falloffExponent; - - Light() {} - void Read(Value& obj, Asset& r); - - void SetDefaults(); - }; - struct Animation : public Object { struct AnimSampler { @@ -1152,7 +1108,7 @@ namespace glTF2 struct Extensions { bool KHR_binary_glTF; - bool KHR_materials_common; + bool KHR_materials_pbrSpecularGlossiness; } extensionsUsed; @@ -1170,16 +1126,11 @@ namespace glTF2 LazyDict materials; LazyDict meshes; LazyDict nodes; - //LazyDict programs; LazyDict samplers; LazyDict scenes; - //LazyDict shaders; LazyDict skins; - //LazyDict techniques; LazyDict textures; - LazyDict lights; // KHR_materials_common ext - Ref scene; public: @@ -1195,14 +1146,10 @@ namespace glTF2 , materials (*this, "materials") , meshes (*this, "meshes") , nodes (*this, "nodes") - //, programs (*this, "programs") , samplers (*this, "samplers") , scenes (*this, "scenes") - //, shaders (*this, "shaders") - , skins (*this, "skins") - //, techniques (*this, "techniques") + , skins (*this, "skins") , textures (*this, "textures") - , lights (*this, "lights", "KHR_materials_common") { memset(&extensionsUsed, 0, sizeof(extensionsUsed)); } diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 94858044d..3d53bfccf 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -757,12 +757,12 @@ inline void Material::Read(Value& material, Asset& r) { SetDefaults(); - if (Value* values = FindObject(material, "pbrMetallicRoughness")) { - ReadMember(*values, "baseColorFactor", this->baseColorFactor); - ReadTextureProperty(r, *values, "baseColorTexture", this->baseColorTexture); - ReadTextureProperty(r, *values, "metallicRoughnessTexture", this->metallicRoughnessTexture); - ReadMember(*values, "metallicFactor", this->metallicFactor); - ReadMember(*values, "roughnessFactor", this->roughnessFactor); + if (Value* pbrMetallicRoughness = FindObject(material, "pbrMetallicRoughness")) { + ReadMember(*pbrMetallicRoughness, "baseColorFactor", this->baseColorFactor); + ReadTextureProperty(r, *pbrMetallicRoughness, "baseColorTexture", this->baseColorTexture); + ReadTextureProperty(r, *pbrMetallicRoughness, "metallicRoughnessTexture", this->metallicRoughnessTexture); + ReadMember(*pbrMetallicRoughness, "metallicFactor", this->metallicFactor); + ReadMember(*pbrMetallicRoughness, "roughnessFactor", this->roughnessFactor); } ReadTextureProperty(r, material, "normalTexture", this->normalTexture); @@ -774,30 +774,17 @@ inline void Material::Read(Value& material, Asset& r) ReadMember(material, "alphaMode", this->alphaMode); ReadMember(material, "alphaCutoff", this->alphaCutoff); - /* if (Value* extensions = FindObject(material, "extensions")) { - if (r.extensionsUsed.KHR_materials_common) { - if (Value* ext = FindObject(*extensions, "KHR_materials_common")) { - if (Value* tnq = FindString(*ext, "technique")) { - const char* t = tnq->GetString(); - if (strcmp(t, "BLINN") == 0) technique = Technique_BLINN; - else if (strcmp(t, "PHONG") == 0) technique = Technique_PHONG; - else if (strcmp(t, "LAMBERT") == 0) technique = Technique_LAMBERT; - else if (strcmp(t, "CONSTANT") == 0) technique = Technique_CONSTANT; - } - - if (Value* values = FindObject(*ext, "values")) { - ReadTextureProperty(r, *values, "ambient", this->ambient); - ReadTextureProperty(r, *values, "diffuse", this->diffuse); - ReadTextureProperty(r, *values, "specular", this->specular); - - ReadMember(*values, "doubleSided", doubleSided); - ReadMember(*values, "transparent", transparent); - ReadMember(*values, "transparency", transparency); - ReadMember(*values, "shininess", shininess); - } + if (Value* extensions = FindObject(material, "extensions")) { + if (r.extensionsUsed.KHR_materials_pbrSpecularGlossiness) { + if (Value* pbrSpecularGlossiness = FindObject(*extensions, "KHR_materials_pbrSpecularGlossiness")) { + ReadMember(*pbrSpecularGlossiness, "diffuseFactor", this->diffuseFactor); + ReadTextureProperty(r, *pbrSpecularGlossiness, "diffuseTexture", this->diffuseTexture); + ReadTextureProperty(r, *pbrSpecularGlossiness, "specularGlossinessTexture", this->specularGlossinessTexture); + ReadMember(*pbrSpecularGlossiness, "specularFactor", this->specularFactor); + ReadMember(*pbrSpecularGlossiness, "glossinessFactor", this->glossinessFactor); } } - } */ + } } namespace { @@ -820,7 +807,10 @@ inline void Material::SetDefaults() alphaCutoff = 0.5; doubleSided = false; - technique = Technique_undefined; + //pbrSpecularGlossiness properties + SetVector(diffuseFactor, 1, 1, 1, 1); + SetVector(specularFactor, 1, 1, 1); + glossinessFactor = 1.0; } namespace { @@ -1083,20 +1073,6 @@ inline void Node::Read(Value& obj, Asset& r) if (this->camera) this->camera->id = this->id; } - - // TODO load "skeletons", "skin", "jointName" - - /*if (Value* extensions = FindObject(obj, "extensions")) { - if (r.extensionsUsed.KHR_materials_common) { - - if (Value* ext = FindObject(*extensions, "KHR_materials_common")) { - if (Value* light = FindUInt(*ext, "light")) { - this->light = r.lights.Retrieve(light->GetUint()); - } - } - - } - }*/ } inline void Scene::Read(Value& obj, Asset& r) @@ -1293,7 +1269,7 @@ inline void Asset::ReadExtensionsUsed(Document& doc) if (exts.find(#EXT) != exts.end()) extensionsUsed.EXT = true; CHECK_EXT(KHR_binary_glTF); - CHECK_EXT(KHR_materials_common); + CHECK_EXT(KHR_materials_pbrSpecularGlossiness); #undef CHECK_EXT } diff --git a/code/glTF2AssetWriter.h b/code/glTF2AssetWriter.h index 976f23f9a..97efbeab5 100644 --- a/code/glTF2AssetWriter.h +++ b/code/glTF2AssetWriter.h @@ -44,7 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * glTF Extensions Support: * KHR_binary_glTF: full - * KHR_materials_common: full + * KHR_materials_pbrSpecularGlossiness: full */ #ifndef GLTF2ASSETWRITER_H_INC #define GLTF2ASSETWRITER_H_INC diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index bd16a7023..33625816e 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -327,23 +327,35 @@ namespace glTF2 { obj.AddMember("doubleSided", m.doubleSided, w.mAl); } - /*Value v; - v.SetObject(); + Value pbrSpecularGlossiness; + pbrSpecularGlossiness.SetObject(); { - if (m.transparent && !m.diffuse.texture) { - m.diffuse.color[3] = m.transparency; + //pbrSpecularGlossiness + + vec4 defaultDiffuseFactor = {1, 1, 1, 1}; + WriteVec(pbrSpecularGlossiness, m.diffuseFactor, "diffuseFactor", defaultDiffuseFactor, w.mAl); + + vec3 defaultSpecularFactor = {1, 1, 1}; + WriteVec(pbrSpecularGlossiness, m.specularFactor, "specularFactor", defaultSpecularFactor, w.mAl); + + if (m.glossinessFactor != 1) { + WriteFloat(obj, m.glossinessFactor, "glossinessFactor", w.mAl); } - WriteVecOrTex(v, m.ambient, m.ambient.texture ? "ambientTexture" : "ambientFactor", w.mAl); - WriteVecOrTex(v, m.diffuse, m.diffuse.texture ? "diffuseTexture" : "diffuseFactor", w.mAl); - WriteVecOrTex(v, m.specular, m.specular.texture ? "specularTexture" : "specularFactor", w.mAl); - WriteVecOrTex(v, m.emission, m.emission.texture ? "emissionTexture" : "emissionFactor", w.mAl); - v.AddMember("shininessFactor", m.shininess, w.mAl); + + WriteTex(obj, m.diffuseTexture, "diffuseTexture", w.mAl); + WriteTex(obj, m.specularGlossinessTexture, "specularGlossinessTexture", w.mAl); } - v.AddMember("type", "commonPhong", w.mAl); + Value ext; ext.SetObject(); - ext.AddMember("KHR_materials_common", v, w.mAl); - obj.AddMember("extensions", ext, w.mAl);*/ + + if (!pbrSpecularGlossiness.ObjectEmpty()) { + ext.AddMember("KHR_materials_pbrSpecularGlossiness", pbrSpecularGlossiness, w.mAl); + } + + if (!ext.ObjectEmpty()) { + obj.AddMember("extensions", ext, w.mAl); + } } namespace { @@ -714,11 +726,11 @@ namespace glTF2 { Value exts; exts.SetArray(); { - if (false) - exts.PushBack(StringRef("KHR_binary_glTF"), mAl); + //if (false) + // exts.PushBack(StringRef("KHR_binary_glTF"), mAl); // This is used to export common materials with GLTF 2. - //exts.PushBack(StringRef("KHR_materials_common"), mAl); + //exts.PushBack(StringRef("KHR_materials_pbrSpecularGlossiness"), mAl); } if (!exts.Empty()) diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index b1bbf8349..316970fa5 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -237,6 +237,13 @@ void glTF2Importer::ImportMaterials(glTF2::Asset& r) aimat->AddProperty(&mat.doubleSided, 1, AI_MATKEY_TWOSIDED); aimat->AddProperty(&mat.alphaMode, 1, "$mat.gltf.alphaMode"); aimat->AddProperty(&mat.alphaCutoff, 1, "$mat.gltf.alphaCutoff"); + + //pbrSpecularGlossiness + SetMaterialColorProperty(r, mat.diffuseFactor, aimat, "$clr.diffuse", 0, 1); + SetMaterialColorProperty(r, mat.specularFactor, aimat, "$clr.specular", 0, 1); + aimat->AddProperty(&mat.glossinessFactor, 1, "$mat.gltf.glossinessFactor"); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.diffuseTexture, aimat, aiTextureType_DIFFUSE, 1); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.specularGlossinessTexture, aimat, aiTextureType_UNKNOWN, 1); } } From 7532d6aac1f18aba3b7e33e7f706c143b6430d4d Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Thu, 31 Aug 2017 18:26:50 -0400 Subject: [PATCH 027/490] Remove Light, Technique references --- code/glTF2Asset.h | 23 ------------------- code/glTF2Asset.inl | 46 -------------------------------------- code/glTF2AssetWriter.inl | 10 --------- code/glTF2Importer.cpp | 47 ++------------------------------------- 4 files changed, 2 insertions(+), 124 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index b19de747a..a4b833f58 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -129,7 +129,6 @@ namespace glTF2 struct BufferView; // here due to cross-reference struct Texture; - struct Light; struct Skin; // Vec/matrix types, as raw float arrays @@ -835,7 +834,6 @@ namespace glTF2 Nullable scale; Ref camera; - Ref light; std::vector< Ref > skeletons; //!< The ID of skeleton nodes. Each of which is the root of a node hierarchy. Ref skin; //!< The ID of the skin referenced by this node. @@ -891,27 +889,6 @@ namespace glTF2 void Read(Value& obj, Asset& r); }; - struct Technique : public Object - { - struct Parameters - { - - }; - - struct States - { - - }; - - struct Functions - { - - }; - - Technique() {} - void Read(Value& obj, Asset& r); - }; - //! A texture and its sampler. struct Texture : public Object { diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 3d53bfccf..eab0b8373 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -984,52 +984,6 @@ inline void Camera::Read(Value& obj, Asset& r) } } -inline void Light::Read(Value& obj, Asset& r) -{ - SetDefaults(); - - if (Value* type = FindString(obj, "type")) { - const char* t = type->GetString(); - if (strcmp(t, "ambient") == 0) this->type = Type_ambient; - else if (strcmp(t, "directional") == 0) this->type = Type_directional; - else if (strcmp(t, "point") == 0) this->type = Type_point; - else if (strcmp(t, "spot") == 0) this->type = Type_spot; - - if (this->type != Type_undefined) { - if (Value* vals = FindString(obj, t)) { - ReadMember(*vals, "color", color); - - ReadMember(*vals, "constantAttenuation", constantAttenuation); - ReadMember(*vals, "linearAttenuation", linearAttenuation); - ReadMember(*vals, "quadraticAttenuation", quadraticAttenuation); - ReadMember(*vals, "distance", distance); - - ReadMember(*vals, "falloffAngle", falloffAngle); - ReadMember(*vals, "falloffExponent", falloffExponent); - } - } - } -} - -inline void Light::SetDefaults() -{ - #ifndef M_PI - const float M_PI = 3.14159265358979323846f; - #endif - - type = Type_undefined; - - SetVector(color, 0.f, 0.f, 0.f, 1.f); - - constantAttenuation = 0.f; - linearAttenuation = 1.f; - quadraticAttenuation = 1.f; - distance = 0.f; - - falloffAngle = static_cast(M_PI / 2.f); - falloffExponent = 0.f; -} - inline void Node::Read(Value& obj, Asset& r) { diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index 33625816e..a8ee16c82 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -557,11 +557,6 @@ namespace glTF2 { } - inline void Write(Value& obj, Technique& b, AssetWriter& w) - { - - } - inline void Write(Value& obj, Texture& tex, AssetWriter& w) { if (tex.source) { @@ -572,11 +567,6 @@ namespace glTF2 { } } - inline void Write(Value& obj, Light& b, AssetWriter& w) - { - - } - inline AssetWriter::AssetWriter(Asset& a) : mDoc() diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 316970fa5..b4fd96948 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -493,46 +493,6 @@ void glTF2Importer::ImportCameras(glTF2::Asset& r) } } -void glTF2Importer::ImportLights(glTF2::Asset& r) -{ - if (!r.lights.Size()) return; - - mScene->mNumLights = r.lights.Size(); - mScene->mLights = new aiLight*[r.lights.Size()]; - - for (size_t i = 0; i < r.lights.Size(); ++i) { - Light& l = r.lights[i]; - - aiLight* ail = mScene->mLights[i] = new aiLight(); - - switch (l.type) { - case Light::Type_directional: - ail->mType = aiLightSource_DIRECTIONAL; break; - - case Light::Type_spot: - ail->mType = aiLightSource_SPOT; break; - - case Light::Type_ambient: - ail->mType = aiLightSource_AMBIENT; break; - - default: // Light::Type_point - ail->mType = aiLightSource_POINT; break; - } - - CopyValue(l.color, ail->mColorAmbient); - CopyValue(l.color, ail->mColorDiffuse); - CopyValue(l.color, ail->mColorSpecular); - - ail->mAngleOuterCone = l.falloffAngle; - ail->mAngleInnerCone = l.falloffExponent; // TODO fix this, it does not look right at all - - ail->mAttenuationConstant = l.constantAttenuation; - ail->mAttenuationLinear = l.linearAttenuation; - ail->mAttenuationQuadratic = l.quadraticAttenuation; - } -} - - aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector& meshOffsets, glTF2::Ref& ptr) { Node& node = *ptr; @@ -602,10 +562,6 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector& pScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName; } - if (node.light) { - pScene->mLights[node.light.GetIndex()]->mName = ainode->mName; - } - return ainode; } @@ -695,13 +651,14 @@ void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IO // Copy the data out // + + ImportEmbeddedTextures(asset); ImportMaterials(asset); ImportMeshes(asset); ImportCameras(asset); - ImportLights(asset); ImportNodes(asset); From 562920fbb8811bd8b47eb9eb5755ad25c0b98973 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Thu, 31 Aug 2017 18:30:43 -0400 Subject: [PATCH 028/490] Changes to GLTF2 materials pbrMetallicRoughness and pbrSpecularGlossiness as structs; persist textureinfo properties from start to finish; persist pbrSpecularGlossiness (via extensionsUsed) usage from start to finish --- code/glTF2Asset.h | 27 +++++++--- code/glTF2Asset.inl | 36 +++++++------ code/glTF2AssetWriter.inl | 64 +++++++++++----------- code/glTF2Exporter.cpp | 109 +++++++++++++++++++++++++++++++++----- code/glTF2Exporter.h | 8 +++ code/glTF2Importer.cpp | 48 +++++++++-------- 6 files changed, 200 insertions(+), 92 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index a4b833f58..3faea0938 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -692,15 +692,30 @@ namespace glTF2 vec4 color; };*/ - //! The material appearance of a primitive. - struct Material : public Object + struct PbrMetallicRoughness { - //PBR metallic roughness properties vec4 baseColorFactor; TextureInfo baseColorTexture; TextureInfo metallicRoughnessTexture; float metallicFactor; float roughnessFactor; + }; + + struct PbrSpecularGlossiness + { + bool on = false; + vec4 diffuseFactor; + vec3 specularFactor; + float glossinessFactor; + TextureInfo diffuseTexture; + TextureInfo specularGlossinessTexture; + }; + + //! The material appearance of a primitive. + struct Material : public Object + { + //PBR metallic roughness properties + PbrMetallicRoughness pbrMetallicRoughness; //other basic material properties NormalTextureInfo normalTexture; @@ -712,11 +727,7 @@ namespace glTF2 bool doubleSided; //extension: KHR_materials_pbrSpecularGlossiness - vec4 diffuseFactor; - vec3 specularFactor; - float glossinessFactor; - TextureInfo diffuseTexture; - TextureInfo specularGlossinessTexture; + PbrSpecularGlossiness pbrSpecularGlossiness; Material() { SetDefaults(); } void Read(Value& obj, Asset& r); diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index eab0b8373..632401576 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -758,11 +758,11 @@ inline void Material::Read(Value& material, Asset& r) SetDefaults(); if (Value* pbrMetallicRoughness = FindObject(material, "pbrMetallicRoughness")) { - ReadMember(*pbrMetallicRoughness, "baseColorFactor", this->baseColorFactor); - ReadTextureProperty(r, *pbrMetallicRoughness, "baseColorTexture", this->baseColorTexture); - ReadTextureProperty(r, *pbrMetallicRoughness, "metallicRoughnessTexture", this->metallicRoughnessTexture); - ReadMember(*pbrMetallicRoughness, "metallicFactor", this->metallicFactor); - ReadMember(*pbrMetallicRoughness, "roughnessFactor", this->roughnessFactor); + ReadMember(*pbrMetallicRoughness, "baseColorFactor", this->pbrMetallicRoughness.baseColorFactor); + ReadTextureProperty(r, *pbrMetallicRoughness, "baseColorTexture", this->pbrMetallicRoughness.baseColorTexture); + ReadTextureProperty(r, *pbrMetallicRoughness, "metallicRoughnessTexture", this->pbrMetallicRoughness.metallicRoughnessTexture); + ReadMember(*pbrMetallicRoughness, "metallicFactor", this->pbrMetallicRoughness.metallicFactor); + ReadMember(*pbrMetallicRoughness, "roughnessFactor", this->pbrMetallicRoughness.roughnessFactor); } ReadTextureProperty(r, material, "normalTexture", this->normalTexture); @@ -777,11 +777,13 @@ inline void Material::Read(Value& material, Asset& r) if (Value* extensions = FindObject(material, "extensions")) { if (r.extensionsUsed.KHR_materials_pbrSpecularGlossiness) { if (Value* pbrSpecularGlossiness = FindObject(*extensions, "KHR_materials_pbrSpecularGlossiness")) { - ReadMember(*pbrSpecularGlossiness, "diffuseFactor", this->diffuseFactor); - ReadTextureProperty(r, *pbrSpecularGlossiness, "diffuseTexture", this->diffuseTexture); - ReadTextureProperty(r, *pbrSpecularGlossiness, "specularGlossinessTexture", this->specularGlossinessTexture); - ReadMember(*pbrSpecularGlossiness, "specularFactor", this->specularFactor); - ReadMember(*pbrSpecularGlossiness, "glossinessFactor", this->glossinessFactor); + this->pbrSpecularGlossiness.on = true; + + ReadMember(*pbrSpecularGlossiness, "diffuseFactor", this->pbrSpecularGlossiness.diffuseFactor); + ReadTextureProperty(r, *pbrSpecularGlossiness, "diffuseTexture", this->pbrSpecularGlossiness.diffuseTexture); + ReadTextureProperty(r, *pbrSpecularGlossiness, "specularGlossinessTexture", this->pbrSpecularGlossiness.specularGlossinessTexture); + ReadMember(*pbrSpecularGlossiness, "specularFactor", this->pbrSpecularGlossiness.specularFactor); + ReadMember(*pbrSpecularGlossiness, "glossinessFactor", this->pbrSpecularGlossiness.glossinessFactor); } } } @@ -798,19 +800,19 @@ namespace { inline void Material::SetDefaults() { //pbr materials - SetVector(baseColorFactor, 1, 1, 1, 1); - SetVector(emissiveFactor, 0, 0, 0); - metallicFactor = 1.0; - roughnessFactor = 1.0; + SetVector(pbrMetallicRoughness.baseColorFactor, 1, 1, 1, 1); + pbrMetallicRoughness.metallicFactor = 1.0; + pbrMetallicRoughness.roughnessFactor = 1.0; + SetVector(emissiveFactor, 0, 0, 0); alphaMode = "OPAQUE"; alphaCutoff = 0.5; doubleSided = false; //pbrSpecularGlossiness properties - SetVector(diffuseFactor, 1, 1, 1, 1); - SetVector(specularFactor, 1, 1, 1); - glossinessFactor = 1.0; + SetVector(pbrSpecularGlossiness.diffuseFactor, 1, 1, 1, 1); + SetVector(pbrSpecularGlossiness.specularFactor, 1, 1, 1); + pbrSpecularGlossiness.glossinessFactor = 1.0; } namespace { diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index a8ee16c82..ce2e7c41d 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -287,19 +287,19 @@ namespace glTF2 { Value pbrMetallicRoughness; pbrMetallicRoughness.SetObject(); { - WriteTex(pbrMetallicRoughness, m.baseColorTexture, "baseColorTexture", w.mAl); - WriteTex(pbrMetallicRoughness, m.metallicRoughnessTexture, "metallicRoughnessTexture", w.mAl); + WriteTex(pbrMetallicRoughness, m.pbrMetallicRoughness.baseColorTexture, "baseColorTexture", w.mAl); + WriteTex(pbrMetallicRoughness, m.pbrMetallicRoughness.metallicRoughnessTexture, "metallicRoughnessTexture", w.mAl); //@TODO: define this as a constant? vec4 defaultEmissiveFactor = {1, 1, 1, 1}; - WriteVec(pbrMetallicRoughness, m.baseColorFactor, "baseColorFactor", defaultEmissiveFactor, w.mAl); + WriteVec(pbrMetallicRoughness, m.pbrMetallicRoughness.baseColorFactor, "baseColorFactor", defaultEmissiveFactor, w.mAl); - if (m.metallicFactor != 1) { - WriteFloat(pbrMetallicRoughness, m.metallicFactor, "metallicFactor", w.mAl); + if (m.pbrMetallicRoughness.metallicFactor != 1) { + WriteFloat(pbrMetallicRoughness, m.pbrMetallicRoughness.metallicFactor, "metallicFactor", w.mAl); } - if (m.roughnessFactor != 1) { - WriteFloat(pbrMetallicRoughness, m.roughnessFactor, "roughnessFactor", w.mAl); + if (m.pbrMetallicRoughness.roughnessFactor != 1) { + WriteFloat(pbrMetallicRoughness, m.pbrMetallicRoughness.roughnessFactor, "roughnessFactor", w.mAl); } } @@ -327,34 +327,36 @@ namespace glTF2 { obj.AddMember("doubleSided", m.doubleSided, w.mAl); } - Value pbrSpecularGlossiness; - pbrSpecularGlossiness.SetObject(); - { - //pbrSpecularGlossiness + Value exts; + exts.SetObject(); - vec4 defaultDiffuseFactor = {1, 1, 1, 1}; - WriteVec(pbrSpecularGlossiness, m.diffuseFactor, "diffuseFactor", defaultDiffuseFactor, w.mAl); + if (m.pbrSpecularGlossiness.on) { + Value pbrSpecularGlossiness; + pbrSpecularGlossiness.SetObject(); + { + //pbrSpecularGlossiness - vec3 defaultSpecularFactor = {1, 1, 1}; - WriteVec(pbrSpecularGlossiness, m.specularFactor, "specularFactor", defaultSpecularFactor, w.mAl); + vec4 defaultDiffuseFactor = {1, 1, 1, 1}; + WriteVec(pbrSpecularGlossiness, m.pbrSpecularGlossiness.diffuseFactor, "diffuseFactor", defaultDiffuseFactor, w.mAl); - if (m.glossinessFactor != 1) { - WriteFloat(obj, m.glossinessFactor, "glossinessFactor", w.mAl); + vec3 defaultSpecularFactor = {1, 1, 1}; + WriteVec(pbrSpecularGlossiness, m.pbrSpecularGlossiness.specularFactor, "specularFactor", defaultSpecularFactor, w.mAl); + + if (m.pbrSpecularGlossiness.glossinessFactor != 1) { + WriteFloat(obj, m.pbrSpecularGlossiness.glossinessFactor, "glossinessFactor", w.mAl); + } + + WriteTex(obj, m.pbrSpecularGlossiness.diffuseTexture, "diffuseTexture", w.mAl); + WriteTex(obj, m.pbrSpecularGlossiness.specularGlossinessTexture, "specularGlossinessTexture", w.mAl); } - WriteTex(obj, m.diffuseTexture, "diffuseTexture", w.mAl); - WriteTex(obj, m.specularGlossinessTexture, "specularGlossinessTexture", w.mAl); + if (!pbrSpecularGlossiness.ObjectEmpty()) { + exts.AddMember("KHR_materials_pbrSpecularGlossiness", pbrSpecularGlossiness, w.mAl); + } } - Value ext; - ext.SetObject(); - - if (!pbrSpecularGlossiness.ObjectEmpty()) { - ext.AddMember("KHR_materials_pbrSpecularGlossiness", pbrSpecularGlossiness, w.mAl); - } - - if (!ext.ObjectEmpty()) { - obj.AddMember("extensions", ext, w.mAl); + if (!exts.ObjectEmpty()) { + obj.AddMember("extensions", exts, w.mAl); } } @@ -719,8 +721,10 @@ namespace glTF2 { //if (false) // exts.PushBack(StringRef("KHR_binary_glTF"), mAl); - // This is used to export common materials with GLTF 2. - //exts.PushBack(StringRef("KHR_materials_pbrSpecularGlossiness"), mAl); + // This is used to export pbrSpecularGlossiness materials with GLTF 2. + if (this->mAsset.extensionsUsed.KHR_materials_pbrSpecularGlossiness) { + exts.PushBack(StringRef("KHR_materials_pbrSpecularGlossiness"), mAl); + } } if (!exts.Empty()) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index f59c280f1..9e04da76c 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -112,9 +112,9 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai ExportMetadata(); - //for (unsigned int i = 0; i < pScene->mNumCameras; ++i) {} - - //for (unsigned int i = 0; i < pScene->mNumLights; ++i) {} + if (mScene->mRootNode) { + ExportExtensions(mScene->mRootNode); + } ExportMaterials(); @@ -124,8 +124,6 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai ExportMeshes(); - //for (unsigned int i = 0; i < pScene->mNumTextures; ++i) {} - ExportScene(); //ExportAnimations(); @@ -280,10 +278,26 @@ void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture) texture->sampler->minFilter = SamplerMinFilter_Linear_Mipmap_Linear; } +void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int slot) +{ + const char* key = (std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName).c_str(); + + mat->Get(key, tt, slot, prop); +} + +void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int slot) +{ + const char* key = (std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName).c_str(); + + mat->Get(key, tt, slot, prop); +} + void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) { - aiString tex; + if (mat->GetTextureCount(tt) > 0) { + aiString tex; + if (mat->Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) { std::string path = tex.C_Str(); @@ -326,6 +340,41 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe } } +void glTF2Exporter::GetMatTex(const aiMaterial* mat, TextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +{ + Ref& texture = prop.texture; + + GetMatTex(mat, texture, tt, slot); + + if (texture) { + GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + } +} + +void glTF2Exporter::GetMatTex(const aiMaterial* mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +{ + Ref& texture = prop.texture; + + GetMatTex(mat, texture, tt, slot); + + if (texture) { + GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + GetMatTexProp(mat, prop.scale, "scale", tt, slot); + } +} + +void glTF2Exporter::GetMatTex(const aiMaterial* mat, OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +{ + Ref& texture = prop.texture; + + GetMatTex(mat, texture, tt, slot); + + if (texture) { + GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + GetMatTexProp(mat, prop.strength, "strength", tt, slot); + } +} + void glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const char* propName, int type, int idx) { aiColor4D col; @@ -344,6 +393,8 @@ void glTF2Exporter::GetMatColor(const aiMaterial* mat, vec3& prop, const char* p void glTF2Exporter::ExportMaterials() { + bool& KHR_materials_pbrSpecularGlossiness = mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness; + aiString aiName; for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) { const aiMaterial* mat = mScene->mMaterials[i]; @@ -356,20 +407,36 @@ void glTF2Exporter::ExportMaterials() Ref m = mAsset->materials.Create(name); - GetMatTex(mat, m->baseColorTexture.texture, aiTextureType_DIFFUSE); - GetMatTex(mat, m->metallicRoughnessTexture.texture, aiTextureType_UNKNOWN, 0);//get unknown slot - GetMatTex(mat, m->emissiveTexture.texture, aiTextureType_EMISSIVE); - GetMatTex(mat, m->normalTexture.texture, aiTextureType_NORMALS); - GetMatTex(mat, m->occlusionTexture.texture, aiTextureType_LIGHTMAP); + GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_DIFFUSE); + GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, aiTextureType_UNKNOWN, 0);//get unknown slot + GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); - GetMatColor(mat, m->baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); + GetMatTex(mat, m->normalTexture, aiTextureType_NORMALS); + GetMatTex(mat, m->occlusionTexture, aiTextureType_LIGHTMAP); + GetMatTex(mat, m->emissiveTexture, aiTextureType_EMISSIVE); GetMatColor(mat, m->emissiveFactor, AI_MATKEY_COLOR_EMISSIVE); mat->Get(AI_MATKEY_TWOSIDED, m->doubleSided); mat->Get("$mat.gltf.alphaCutoff", 0, 0, m->alphaCutoff); - mat->Get("$mat.gltf.metallicFactor", 0, 0, m->metallicFactor); - mat->Get("$mat.gltf.roughnessFactor", 0, 0, m->roughnessFactor); + mat->Get("$mat.gltf.metallicFactor", 0, 0, m->pbrMetallicRoughness.metallicFactor); + mat->Get("$mat.gltf.roughnessFactor", 0, 0, m->pbrMetallicRoughness.roughnessFactor); mat->Get("$mat.gltf.alphaMode", 0, 0, m->alphaMode); + + bool hasPbrSpecularGlossiness; + mat->Get("$mat.gltf.pbrSpecularGlossiness.on", 0, 0, hasPbrSpecularGlossiness); + + if (hasPbrSpecularGlossiness) { + + if (!KHR_materials_pbrSpecularGlossiness) { + KHR_materials_pbrSpecularGlossiness = true; + } + + GetMatColor(mat, m->pbrSpecularGlossiness.diffuseFactor, "$clr.diffuse", 0, 1); + GetMatColor(mat, m->pbrSpecularGlossiness.specularFactor, "$clr.specular", 0, 1); + mat->Get("$mat.gltf.glossinessFactor", 0, 0, m->pbrSpecularGlossiness.glossinessFactor); + GetMatTex(mat, m->pbrSpecularGlossiness.diffuseTexture, aiTextureType_DIFFUSE, 1); + GetMatTex(mat, m->pbrSpecularGlossiness.specularGlossinessTexture, aiTextureType_UNKNOWN, 1); + } } } @@ -862,6 +929,20 @@ void glTF2Exporter::ExportMetadata() asset.generator = buffer; } +void glTF2Exporter::ExportExtensions(const aiNode* n) +{ + aiMetadata* mMetaData = n->mMetaData; + + if (mMetaData != nullptr) { + bool pbrSpecularGlossiness; + + if (mMetaData->Get("extensionsUsed.pbrSpecularGlossiness", pbrSpecularGlossiness)) { + mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = pbrSpecularGlossiness; + } + } + +} + inline void ExtractAnimationData(Asset& mAsset, std::string& animId, Ref& animRef, Ref& buffer, const aiNodeAnim* nodeChannel, float ticksPerSecond) { // Loop over the data and check to see if it exactly matches an existing buffer. diff --git a/code/glTF2Exporter.h b/code/glTF2Exporter.h index 2ba0143bf..827189f29 100644 --- a/code/glTF2Exporter.h +++ b/code/glTF2Exporter.h @@ -67,6 +67,8 @@ namespace glTF2 class Asset; struct TexProperty; struct TextureInfo; + struct NormalTextureInfo; + struct OcclusionTextureInfo; struct Node; struct Texture; @@ -107,10 +109,16 @@ namespace Assimp void WriteBinaryData(IOStream* outfile, std::size_t sceneLength); void GetTexSampler(const aiMaterial* mat, glTF2::Ref texture); + void GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx); + void GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx); void GetMatTex(const aiMaterial* mat, glTF2::Ref& texture, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial* mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial* mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial* mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot); void GetMatColor(const aiMaterial* mat, glTF2::vec4& prop, const char* propName, int type, int idx); void GetMatColor(const aiMaterial* mat, glTF2::vec3& prop, const char* propName, int type, int idx); void ExportMetadata(); + void ExportExtensions(const aiNode* n); void ExportMaterials(); void ExportMeshes(); unsigned int ExportNodeHierarchy(const aiNode* n); diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index b4fd96948..97901fc01 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -183,7 +183,7 @@ inline void SetMaterialColorProperty(Asset& r, vec3& prop, aiMaterial* mat, cons return SetMaterialColorProperty(r, prop4, mat, pKey, type, idx); } -inline void SetMaterialTextureProperty(std::vector& embeddedTexIdxs, Asset& r, glTF2::TextureInfo prop, aiMaterial* mat, aiTextureType texType, int texSlot) +inline void SetMaterialTextureProperty(std::vector& embeddedTexIdxs, Asset& r, glTF2::TextureInfo prop, aiMaterial* mat, aiTextureType texType, unsigned int texSlot = 0) { if (prop.texture && prop.texture->source) { aiString uri(prop.texture->source->uri); @@ -195,19 +195,11 @@ inline void SetMaterialTextureProperty(std::vector& embeddedTexIdxs, Asset& uri.length = 1 + ASSIMP_itoa10(uri.data + 1, MAXLEN - 1, texIdx); } - if (texSlot < 0) { - mat->AddProperty(&uri, _AI_MATKEY_TEXTURE_BASE, texType, 0); - } - else { - mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, - texSlot)); - } - } -} + mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot)); -inline void SetMaterialTextureProperty(std::vector& embeddedTexIdxs, Asset& r, glTF2::TextureInfo prop, aiMaterial* mat, aiTextureType texType) -{ - return SetMaterialTextureProperty(embeddedTexIdxs, r, prop, mat, texType, -1); + const char *texCoordName = (std::string(_AI_MATKEY_TEXTURE_BASE) + ".texCoord").c_str(); + mat->AddProperty(&prop.texCoord, 1, texCoordName, texType, texSlot); + } } void glTF2Importer::ImportMaterials(glTF2::Asset& r) @@ -223,11 +215,11 @@ void glTF2Importer::ImportMaterials(glTF2::Asset& r) aiString str(mat.id); aimat->AddProperty(&str, AI_MATKEY_NAME); - SetMaterialColorProperty(r, mat.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.baseColorTexture, aimat, aiTextureType_DIFFUSE); - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.metallicRoughnessTexture, aimat, aiTextureType_UNKNOWN, 0); - aimat->AddProperty(&mat.metallicFactor, 1, "$mat.gltf.metallicFactor"); - aimat->AddProperty(&mat.roughnessFactor, 1, "$mat.gltf.roughnessFactor"); + SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, aiTextureType_UNKNOWN); + aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, "$mat.gltf.pbrMetallicRoughness.metallicFactor"); + aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, "$mat.gltf.pbrMetallicRoughness.roughnessFactor"); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.normalTexture, aimat, aiTextureType_NORMALS); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.occlusionTexture, aimat, aiTextureType_LIGHTMAP); @@ -239,11 +231,14 @@ void glTF2Importer::ImportMaterials(glTF2::Asset& r) aimat->AddProperty(&mat.alphaCutoff, 1, "$mat.gltf.alphaCutoff"); //pbrSpecularGlossiness - SetMaterialColorProperty(r, mat.diffuseFactor, aimat, "$clr.diffuse", 0, 1); - SetMaterialColorProperty(r, mat.specularFactor, aimat, "$clr.specular", 0, 1); - aimat->AddProperty(&mat.glossinessFactor, 1, "$mat.gltf.glossinessFactor"); - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.diffuseTexture, aimat, aiTextureType_DIFFUSE, 1); - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.specularGlossinessTexture, aimat, aiTextureType_UNKNOWN, 1); + if (mat.pbrSpecularGlossiness.on) { + aimat->AddProperty(&mat.pbrSpecularGlossiness.on, 1, "$mat.gltf.pbrSpecularGlossiness.on"); + SetMaterialColorProperty(r, mat.pbrSpecularGlossiness.diffuseFactor, aimat, "$clr.diffuse", 0, 1); + SetMaterialColorProperty(r, mat.pbrSpecularGlossiness.specularFactor, aimat, "$clr.specular", 0, 1); + aimat->AddProperty(&mat.pbrSpecularGlossiness.glossinessFactor, 1, "$mat.gltf.pbrSpecularGlossiness.glossinessFactor"); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrSpecularGlossiness.diffuseTexture, aimat, aiTextureType_DIFFUSE, 1); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrSpecularGlossiness.specularGlossinessTexture, aimat, aiTextureType_UNKNOWN, 1); + } } } @@ -590,6 +585,13 @@ void glTF2Importer::ImportNodes(glTF2::Asset& r) //if (!mScene->mRootNode) { // mScene->mRootNode = new aiNode("EMPTY"); //} + + //initialize mMetaData; + aiMetadata* mMetaData = new aiMetadata(); + + //store used glTF extensions on the root node, for a lack of a better place. + mMetaData->Add("extensionsUsed.pbrSpecularGlossiness", r.extensionsUsed.KHR_materials_pbrSpecularGlossiness); + mScene->mRootNode->mMetaData = mMetaData; } void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset& r) From 2d54019b8fb10e46f2b1d4e98e3f1a904de15309 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Thu, 31 Aug 2017 23:38:10 -0400 Subject: [PATCH 029/490] Remove OPEN3DGC and compression references --- code/glTF2Asset.h | 71 ------------------- code/glTF2Asset.inl | 74 -------------------- code/glTF2AssetWriter.inl | 51 -------------- code/glTF2Exporter.cpp | 142 +------------------------------------- code/glTF2Importer.cpp | 32 --------- 5 files changed, 1 insertion(+), 369 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 3faea0938..dd348de1a 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -752,86 +752,15 @@ namespace glTF2 Ref material; }; - /// \struct SExtension - /// Extension used for mesh. - struct SExtension - { - /// \enum EType - /// Type of extension. - enum EType - { - #ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC - Compression_Open3DGC,///< Compression of mesh data using Open3DGC algorithm. - #endif - - Unknown - }; - - EType Type;///< Type of extension. - - /// \fn SExtension - /// Constructor. - /// \param [in] pType - type of extension. - SExtension(const EType pType) - : Type(pType) - {} - - virtual ~SExtension() { - // empty - } - }; - - #ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC - /// \struct SCompression_Open3DGC - /// Compression of mesh data using Open3DGC algorithm. - struct SCompression_Open3DGC : public SExtension - { - using SExtension::Type; - - std::string Buffer;///< ID of "buffer" used for storing compressed data. - size_t Offset;///< Offset in "bufferView" where compressed data are stored. - size_t Count;///< Count of elements in compressed data. Is always equivalent to size in bytes: look comments for "Type" and "Component_Type". - bool Binary;///< If true then "binary" mode is used for coding, if false - "ascii" mode. - size_t IndicesCount;///< Count of indices in mesh. - size_t VerticesCount;///< Count of vertices in mesh. - // AttribType::Value Type;///< Is always "SCALAR". - // ComponentType Component_Type;///< Is always "ComponentType_UNSIGNED_BYTE" (5121). - - /// \fn SCompression_Open3DGC - /// Constructor. - SCompression_Open3DGC() - : SExtension(Compression_Open3DGC) { - // empty - } - - virtual ~SCompression_Open3DGC() { - // empty - } - }; - #endif - std::vector primitives; - std::list Extension;///< List of extensions used in mesh. Mesh() {} - /// \fn ~Mesh() - /// Destructor. - ~Mesh() { for(std::list::iterator it = Extension.begin(), it_end = Extension.end(); it != it_end; it++) { delete *it; }; } - /// \fn void Read(Value& pJSON_Object, Asset& pAsset_Root) /// Get mesh data from JSON-object and place them to root asset. /// \param [in] pJSON_Object - reference to pJSON-object from which data are read. /// \param [out] pAsset_Root - reference to root assed where data will be stored. void Read(Value& pJSON_Object, Asset& pAsset_Root); - - #ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC - /// \fn void Decode_O3DGC(const SCompression_Open3DGC& pCompression_Open3DGC, Asset& pAsset_Root) - /// Decode part of "buffer" which encoded with Open3DGC algorithm. - /// \param [in] pCompression_Open3DGC - reference to structure which describe encoded region. - /// \param [out] pAsset_Root - reference to root assed where data will be stored. - void Decode_O3DGC(const SCompression_Open3DGC& pCompression_Open3DGC, Asset& pAsset_Root); - #endif }; struct Node : public Object diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 632401576..dd110ebb7 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -44,11 +44,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Header files, Assimp #include -#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC - // Header files, Open3DGC. -# include -#endif - using namespace Assimp; namespace glTF2 { @@ -892,75 +887,6 @@ inline void Mesh::Read(Value& pJSON_Object, Asset& pAsset_Root) } } } - - /****************** Mesh extensions ******************/ - Value* json_extensions = FindObject(pJSON_Object, "extensions"); - - if(json_extensions == nullptr) goto mr_skip_extensions; - - for(Value::MemberIterator it_memb = json_extensions->MemberBegin(); it_memb != json_extensions->MemberEnd(); it_memb++) - { -#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC - if(it_memb->name.GetString() == std::string("Open3DGC-compression")) - { - // Search for compressed data. - // Compressed data contain description of part of "buffer" which is encoded. This part must be decoded and - // new data will replace old encoded part by request. In fact \"compressedData\" is kind of "accessor" structure. - Value* comp_data = FindObject(it_memb->value, "compressedData"); - - if(comp_data == nullptr) throw DeadlyImportError("GLTF: \"Open3DGC-compression\" must has \"compressedData\"."); - - DefaultLogger::get()->info("GLTF: Decompressing Open3DGC data."); - - /************** Read data from JSON-document **************/ - #define MESH_READ_COMPRESSEDDATA_MEMBER(pFieldName, pOut) \ - if(!ReadMember(*comp_data, pFieldName, pOut)) \ - { \ - throw DeadlyImportError(std::string("GLTF: \"compressedData\" must has \"") + pFieldName + "\"."); \ - } - - const char* mode_str; - const char* type_str; - ComponentType component_type; - SCompression_Open3DGC* ext_o3dgc = new SCompression_Open3DGC; - - MESH_READ_COMPRESSEDDATA_MEMBER("buffer", ext_o3dgc->Buffer); - MESH_READ_COMPRESSEDDATA_MEMBER("byteOffset", ext_o3dgc->Offset); - MESH_READ_COMPRESSEDDATA_MEMBER("componentType", component_type); - MESH_READ_COMPRESSEDDATA_MEMBER("type", type_str); - MESH_READ_COMPRESSEDDATA_MEMBER("count", ext_o3dgc->Count); - MESH_READ_COMPRESSEDDATA_MEMBER("mode", mode_str); - MESH_READ_COMPRESSEDDATA_MEMBER("indicesCount", ext_o3dgc->IndicesCount); - MESH_READ_COMPRESSEDDATA_MEMBER("verticesCount", ext_o3dgc->VerticesCount); - - #undef MESH_READ_COMPRESSEDDATA_MEMBER - - // Check some values - if(strcmp(type_str, "SCALAR")) throw DeadlyImportError("GLTF: only \"SCALAR\" type is supported for compressed data."); - if(component_type != ComponentType_UNSIGNED_BYTE) throw DeadlyImportError("GLTF: only \"UNSIGNED_BYTE\" component type is supported for compressed data."); - - // Set read/write data mode. - if(strcmp(mode_str, "binary") == 0) - ext_o3dgc->Binary = true; - else if(strcmp(mode_str, "ascii") == 0) - ext_o3dgc->Binary = false; - else - throw DeadlyImportError(std::string("GLTF: for compressed data supported modes is: \"ascii\", \"binary\". Not the: \"") + mode_str + "\"."); - - /************************ Decoding ************************/ - Decode_O3DGC(*ext_o3dgc, pAsset_Root); - Extension.push_back(ext_o3dgc);// store info in mesh extensions list. - }// if(it_memb->name.GetString() == "Open3DGC-compression") - else -#endif - { - throw DeadlyImportError(std::string("GLTF: Unknown mesh extension: \"") + it_memb->name.GetString() + "\"."); - } - }// for(Value::MemberIterator it_memb = json_extensions->MemberBegin(); it_memb != json_extensions->MemberEnd(); json_extensions++) - -mr_skip_extensions: - - return;// After label some operators must be present. } inline void Camera::Read(Value& obj, Asset& r) diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index ce2e7c41d..c74ea4031 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -380,57 +380,6 @@ namespace glTF2 { inline void Write(Value& obj, Mesh& m, AssetWriter& w) { - - /**************** Mesh extensions ****************/ - if(m.Extension.size() > 0) - { - Value json_extensions; - - json_extensions.SetObject(); - for(Mesh::SExtension* ptr_ext : m.Extension) - { - switch(ptr_ext->Type) - { -#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC - case Mesh::SExtension::EType::Compression_Open3DGC: - { - Value json_comp_data; - Mesh::SCompression_Open3DGC* ptr_ext_comp = (Mesh::SCompression_Open3DGC*)ptr_ext; - - // filling object "compressedData" - json_comp_data.SetObject(); - json_comp_data.AddMember("buffer", ptr_ext_comp->Buffer, w.mAl); - json_comp_data.AddMember("byteOffset", ptr_ext_comp->Offset, w.mAl); - json_comp_data.AddMember("componentType", 5121, w.mAl); - json_comp_data.AddMember("type", "SCALAR", w.mAl); - json_comp_data.AddMember("count", ptr_ext_comp->Count, w.mAl); - if(ptr_ext_comp->Binary) - json_comp_data.AddMember("mode", "binary", w.mAl); - else - json_comp_data.AddMember("mode", "ascii", w.mAl); - - json_comp_data.AddMember("indicesCount", ptr_ext_comp->IndicesCount, w.mAl); - json_comp_data.AddMember("verticesCount", ptr_ext_comp->VerticesCount, w.mAl); - // filling object "Open3DGC-compression" - Value json_o3dgc; - - json_o3dgc.SetObject(); - json_o3dgc.AddMember("compressedData", json_comp_data, w.mAl); - // add member to object "extensions" - json_extensions.AddMember("Open3DGC-compression", json_o3dgc, w.mAl); - } - - break; -#endif - default: - throw DeadlyImportError("GLTF: Can not write mesh: unknown mesh extension, only Open3DGC is supported."); - }// switch(ptr_ext->Type) - }// for(Mesh::SExtension* ptr_ext : m.Extension) - - // Add extensions to mesh - obj.AddMember("extensions", json_extensions, w.mAl); - }// if(m.Extension.size() > 0) - /****************** Primitives *******************/ Value primitives; primitives.SetArray(); diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 9e04da76c..2b7e4c33d 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -62,11 +62,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "glTF2AssetWriter.h" -#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC - // Header files, Open3DGC. -# include -#endif - using namespace rapidjson; using namespace Assimp; @@ -573,15 +568,6 @@ void glTF2Exporter::ExportMeshes() // because "ComponentType_UNSIGNED_SHORT" used for indices. And it's a maximal type according to glTF specification. typedef unsigned short IndicesType; - // Variables needed for compression. BEGIN. - // Indices, not pointers - because pointer to buffer is changing while writing to it. - size_t idx_srcdata_begin;// Index of buffer before writing mesh data. Also, index of begin of coordinates array in buffer. - size_t idx_srcdata_normal = SIZE_MAX;// Index of begin of normals array in buffer. SIZE_MAX - mean that mesh has no normals. - std::vector idx_srcdata_tc;// Array of indices. Every index point to begin of texture coordinates array in buffer. - size_t idx_srcdata_ind;// Index of begin of coordinates indices array in buffer. - bool comp_allow;// Point that data of current mesh can be compressed. - // Variables needed for compression. END. - std::string fname = std::string(mFilename); std::string bufferIdPrefix = fname.substr(0, fname.rfind(".gltf")); std::string bufferId = mAsset->FindUniqueID("", bufferIdPrefix.c_str()); @@ -614,31 +600,6 @@ void glTF2Exporter::ExportMeshes() for (unsigned int idx_mesh = 0; idx_mesh < mScene->mNumMeshes; ++idx_mesh) { const aiMesh* aim = mScene->mMeshes[idx_mesh]; - // Check if compressing requested and mesh can be encoded. -#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC - comp_allow = mProperties->GetPropertyBool("extensions.Open3DGC.use", false); -#else - comp_allow = false; -#endif - - if(comp_allow && (aim->mPrimitiveTypes == aiPrimitiveType_TRIANGLE) && (aim->mNumVertices > 0) && (aim->mNumFaces > 0)) - { - idx_srcdata_tc.clear(); - idx_srcdata_tc.reserve(AI_MAX_NUMBER_OF_TEXTURECOORDS); - } - else - { - std::string msg; - - if(aim->mPrimitiveTypes != aiPrimitiveType_TRIANGLE) - msg = "all primitives of the mesh must be a triangles."; - else - msg = "mesh must has vertices and faces."; - - DefaultLogger::get()->warn("GLTF: can not use Open3DGC-compression: " + msg); - comp_allow = false; - } - std::string name = aim->mName.C_Str(); std::string meshId = mAsset->FindUniqueID(name, "mesh"); @@ -651,15 +612,10 @@ void glTF2Exporter::ExportMeshes() p.material = mAsset->materials.Get(aim->mMaterialIndex); /******************* Vertices ********************/ - // If compression is used then you need parameters of uncompressed region: begin and size. At this step "begin" is stored. - if(comp_allow) idx_srcdata_begin = b->byteLength; - Ref v = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mVertices, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT); if (v) p.attributes.position.push_back(v); /******************** Normals ********************/ - if(comp_allow && (aim->mNormals != 0)) idx_srcdata_normal = b->byteLength;// Store index of normals array. - Ref n = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mNormals, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT); if (n) p.attributes.normal.push_back(n); @@ -675,16 +631,12 @@ void glTF2Exporter::ExportMeshes() if (aim->mNumUVComponents[i] > 0) { AttribType::Value type = (aim->mNumUVComponents[i] == 2) ? AttribType::VEC2 : AttribType::VEC3; - if(comp_allow) idx_srcdata_tc.push_back(b->byteLength);// Store index of texture coordinates array. - Ref tc = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mTextureCoords[i], AttribType::VEC3, type, ComponentType_FLOAT, false); if (tc) p.attributes.texcoord.push_back(tc); } } /*************** Vertices indices ****************/ - idx_srcdata_ind = b->byteLength;// Store index of indices array. - if (aim->mNumFaces > 0) { std::vector indices; unsigned int nIndicesPerFace = aim->mFaces[0].mNumIndices; @@ -713,99 +665,7 @@ void glTF2Exporter::ExportMeshes() /*if(aim->HasBones()) { ExportSkin(*mAsset, aim, m, b, skinRef, inverseBindMatricesData); }*/ - - /****************** Compression ******************/ - ///TODO: animation: weights, joints. - if(comp_allow) - { -#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC - // Only one type of compression supported at now - Open3DGC. - // - o3dgc::BinaryStream bs; - o3dgc::SC3DMCEncoder encoder; - o3dgc::IndexedFaceSet comp_o3dgc_ifs; - o3dgc::SC3DMCEncodeParams comp_o3dgc_params; - - // - // Fill data for encoder. - // - // Quantization - unsigned quant_coord = mProperties->GetPropertyInteger("extensions.Open3DGC.quantization.POSITION", 12); - unsigned quant_normal = mProperties->GetPropertyInteger("extensions.Open3DGC.quantization.NORMAL", 10); - unsigned quant_texcoord = mProperties->GetPropertyInteger("extensions.Open3DGC.quantization.TEXCOORD", 10); - - // Prediction - o3dgc::O3DGCSC3DMCPredictionMode prediction_position = o3dgc::O3DGC_SC3DMC_PARALLELOGRAM_PREDICTION; - o3dgc::O3DGCSC3DMCPredictionMode prediction_normal = o3dgc::O3DGC_SC3DMC_SURF_NORMALS_PREDICTION; - o3dgc::O3DGCSC3DMCPredictionMode prediction_texcoord = o3dgc::O3DGC_SC3DMC_PARALLELOGRAM_PREDICTION; - - // IndexedFacesSet: "Crease angle", "solid", "convex" are set to default. - comp_o3dgc_ifs.SetCCW(true); - comp_o3dgc_ifs.SetIsTriangularMesh(true); - comp_o3dgc_ifs.SetNumFloatAttributes(0); - // Coordinates - comp_o3dgc_params.SetCoordQuantBits(quant_coord); - comp_o3dgc_params.SetCoordPredMode(prediction_position); - comp_o3dgc_ifs.SetNCoord(aim->mNumVertices); - comp_o3dgc_ifs.SetCoord((o3dgc::Real* const)&b->GetPointer()[idx_srcdata_begin]); - // Normals - if(idx_srcdata_normal != SIZE_MAX) - { - comp_o3dgc_params.SetNormalQuantBits(quant_normal); - comp_o3dgc_params.SetNormalPredMode(prediction_normal); - comp_o3dgc_ifs.SetNNormal(aim->mNumVertices); - comp_o3dgc_ifs.SetNormal((o3dgc::Real* const)&b->GetPointer()[idx_srcdata_normal]); - } - - // Texture coordinates - for(size_t num_tc = 0; num_tc < idx_srcdata_tc.size(); num_tc++) - { - size_t num = comp_o3dgc_ifs.GetNumFloatAttributes(); - - comp_o3dgc_params.SetFloatAttributeQuantBits(static_cast(num), quant_texcoord); - comp_o3dgc_params.SetFloatAttributePredMode(static_cast(num), prediction_texcoord); - comp_o3dgc_ifs.SetNFloatAttribute(static_cast(num), aim->mNumVertices);// number of elements. - comp_o3dgc_ifs.SetFloatAttributeDim(static_cast(num), aim->mNumUVComponents[num_tc]);// components per element: aiVector3D => x * float - comp_o3dgc_ifs.SetFloatAttributeType(static_cast(num), o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_TEXCOORD); - comp_o3dgc_ifs.SetFloatAttribute(static_cast(num), (o3dgc::Real* const)&b->GetPointer()[idx_srcdata_tc[num_tc]]); - comp_o3dgc_ifs.SetNumFloatAttributes(static_cast(num + 1)); - } - - // Coordinates indices - comp_o3dgc_ifs.SetNCoordIndex(aim->mNumFaces); - comp_o3dgc_ifs.SetCoordIndex((IndicesType* const)&b->GetPointer()[idx_srcdata_ind]); - // Prepare to enconding - comp_o3dgc_params.SetNumFloatAttributes(comp_o3dgc_ifs.GetNumFloatAttributes()); - if(mProperties->GetPropertyBool("extensions.Open3DGC.binary", true)) - comp_o3dgc_params.SetStreamType(o3dgc::O3DGC_STREAM_TYPE_BINARY); - else - comp_o3dgc_params.SetStreamType(o3dgc::O3DGC_STREAM_TYPE_ASCII); - - comp_o3dgc_ifs.ComputeMinMax(o3dgc::O3DGC_SC3DMC_MAX_ALL_DIMS); - // - // Encoding - // - encoder.Encode(comp_o3dgc_params, comp_o3dgc_ifs, bs); - // Replace data in buffer. - b->ReplaceData(idx_srcdata_begin, b->byteLength - idx_srcdata_begin, bs.GetBuffer(), bs.GetSize()); - // - // Add information about extension to mesh. - // - // Create extension structure. - Mesh::SCompression_Open3DGC* ext = new Mesh::SCompression_Open3DGC; - - // Fill it. - ext->Buffer = b->id; - ext->Offset = idx_srcdata_begin; - ext->Count = b->byteLength - idx_srcdata_begin; - ext->Binary = mProperties->GetPropertyBool("extensions.Open3DGC.binary"); - ext->IndicesCount = comp_o3dgc_ifs.GetNCoordIndex() * 3; - ext->VerticesCount = comp_o3dgc_ifs.GetNCoord(); - // And assign to mesh. - m->Extension.push_back(ext); -#endif - }// if(comp_allow) - }// for (unsigned int i = 0; i < mScene->mNumMeshes; ++i) + } //---------------------------------------- // Finish the skin diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 97901fc01..a2c0911bd 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -288,38 +288,6 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r) for (unsigned int m = 0; m < r.meshes.Size(); ++m) { Mesh& mesh = r.meshes[m]; - // Check if mesh extensions is used - if(mesh.Extension.size() > 0) - { - for(Mesh::SExtension* cur_ext : mesh.Extension) - { -#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC - if(cur_ext->Type == Mesh::SExtension::EType::Compression_Open3DGC) - { - // Limitations for meshes when using Open3DGC-compression. - // It's a current limitation of sp... Specification have not this part still - about mesh compression. Why only one primitive? - // Because glTF is very flexibly. But in fact it ugly flexible. Every primitive can has own set of accessors and accessors can - // point to a-a-a-a-any part of buffer (through bufferview of course) and even to another buffer. We know that "Open3DGC-compression" - // is applicable only to part of buffer. As we can't guaranty continuity of the data for decoder, we will limit quantity of primitives. - // Yes indices, coordinates etc. still can br stored in different buffers, but with current specification it's a exporter problem. - // Also primitive can has only one of "POSITION", "NORMAL" and less then "AI_MAX_NUMBER_OF_TEXTURECOORDS" of "TEXCOORD". All accessor - // of primitive must point to one continuous region of the buffer. - if(mesh.primitives.size() > 2) throw DeadlyImportError("GLTF: When using Open3DGC compression then only one primitive per mesh are allowed."); - - Mesh::SCompression_Open3DGC* o3dgc_ext = (Mesh::SCompression_Open3DGC*)cur_ext; - Ref buf = r.buffers.Get(o3dgc_ext->Buffer); - - buf->EncodedRegion_SetCurrent(mesh.id); - } - else -#endif - { - throw DeadlyImportError("GLTF: Can not import mesh: unknown mesh extension (code: \"" + to_string(cur_ext->Type) + - "\"), only Open3DGC is supported."); - } - } - }// if(mesh.Extension.size() > 0) - meshOffsets.push_back(k); k += unsigned(mesh.primitives.size()); From 8bef546b41b1069effb64fd53682172fb7187607 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Thu, 31 Aug 2017 23:40:44 -0400 Subject: [PATCH 030/490] mention pbrSpecularGlossiness support --- code/glTF2Asset.h | 1 + 1 file changed, 1 insertion(+) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index dd348de1a..1f9f48e18 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * glTF Extensions Support: * KHR_binary_glTF: full + * KHR_materials_pbrSpecularGlossiness full */ #ifndef GLTF2ASSET_H_INC #define GLTF2ASSET_H_INC From feee7528d6c8323e0cdb7aeb4c38bb62909d6a51 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Fri, 1 Sep 2017 02:42:09 -0400 Subject: [PATCH 031/490] Make sure `on` flag for specularGlossiness is being persisted --- code/glTF2Exporter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 2b7e4c33d..ae610d16d 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -426,6 +426,7 @@ void glTF2Exporter::ExportMaterials() KHR_materials_pbrSpecularGlossiness = true; } + m->pbrSpecularGlossiness.on = true; GetMatColor(mat, m->pbrSpecularGlossiness.diffuseFactor, "$clr.diffuse", 0, 1); GetMatColor(mat, m->pbrSpecularGlossiness.specularFactor, "$clr.specular", 0, 1); mat->Get("$mat.gltf.glossinessFactor", 0, 0, m->pbrSpecularGlossiness.glossinessFactor); From f09892ab63fcd919f1489bd8853963c020b3ca80 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Fri, 1 Sep 2017 02:42:26 -0400 Subject: [PATCH 032/490] Write specularGlossiness textures on the specularGlossiness object --- code/glTF2AssetWriter.inl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index c74ea4031..9aa00729c 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -346,8 +346,8 @@ namespace glTF2 { WriteFloat(obj, m.pbrSpecularGlossiness.glossinessFactor, "glossinessFactor", w.mAl); } - WriteTex(obj, m.pbrSpecularGlossiness.diffuseTexture, "diffuseTexture", w.mAl); - WriteTex(obj, m.pbrSpecularGlossiness.specularGlossinessTexture, "specularGlossinessTexture", w.mAl); + WriteTex(pbrSpecularGlossiness, m.pbrSpecularGlossiness.diffuseTexture, "diffuseTexture", w.mAl); + WriteTex(pbrSpecularGlossiness, m.pbrSpecularGlossiness.specularGlossinessTexture, "specularGlossinessTexture", w.mAl); } if (!pbrSpecularGlossiness.ObjectEmpty()) { From ab08a7c3cbc27add5c10ea44786018c9b5c8ff47 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Fri, 1 Sep 2017 17:49:04 -0400 Subject: [PATCH 033/490] reenable animation and skins exports Currently incorrect, however. May need to be removed --- code/glTF2Asset.h | 3 +++ code/glTF2Asset.inl | 36 ++++++++++++++++++++++++++---------- code/glTF2Exporter.cpp | 14 +++++++------- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 1f9f48e18..8e3a6dd7a 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -928,9 +928,11 @@ namespace glTF2 friend class AssetWriter; typedef typename std::gltf_unordered_map< unsigned int, unsigned int > Dict; + typedef typename std::gltf_unordered_map< std::string, unsigned int > IdDict; std::vector mObjs; //! The read objects Dict mObjsByOIndex; //! The read objects accessible by original index + IdDict mObjsById; //! The read objects accessible by id const char* mDictId; //! ID of the dictionary object const char* mExtId; //! ID of the extension defining the dictionary Value* mDict; //! JSON dictionary object @@ -951,6 +953,7 @@ namespace glTF2 Ref Retrieve(unsigned int i); Ref Get(unsigned int i); + Ref Get(const char* id); Ref Create(const char* id); Ref Create(const std::string& id) diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index dd110ebb7..5316b5e26 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -1,4 +1,4 @@ -/* +/* Open Asset Import Library (assimp) ---------------------------------------------------------------------- @@ -193,14 +193,6 @@ inline void LazyDict::DetachFromDocument() mDict = 0; } -template -Ref LazyDict::Get(unsigned int i) -{ - - return Ref(mObjs, i); - -} - template Ref LazyDict::Retrieve(unsigned int i) { @@ -234,12 +226,34 @@ Ref LazyDict::Retrieve(unsigned int i) return Add(inst); } +template +Ref LazyDict::Get(unsigned int i) +{ + + return Ref(mObjs, i); + +} + +template +Ref LazyDict::Get(const char* id) +{ + id = T::TranslateId(mAsset, id); + + typename IdDict::iterator it = mObjsById.find(id); + if (it != mObjsById.end()) { // already created? + return Ref(mObjs, it->second); + } + + throw std::out_of_range("id \"" + std::string(id) + "\" Doesn't exist"); +} + template Ref LazyDict::Add(T* obj) { unsigned int idx = unsigned(mObjs.size()); mObjs.push_back(obj); mObjsByOIndex[obj->oIndex] = idx; + mObjsById[obj->id] = idx; mAsset.mUsedIds[obj->id] = true; return Ref(mObjs, idx); } @@ -252,8 +266,10 @@ Ref LazyDict::Create(const char* id) throw DeadlyImportError("GLTF: two objects with the same ID exist"); } T* inst = new T(); + unsigned int idx = unsigned(mObjs.size()); inst->id = id; - inst->index = static_cast(mObjs.size()); + inst->index = idx; + inst->oIndex = idx; return Add(inst); } diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index ae610d16d..164b559fd 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -121,7 +121,7 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai ExportScene(); - //ExportAnimations(); + ExportAnimations(); AssetWriter writer(*mAsset); @@ -481,7 +481,7 @@ Ref FindSkeletonRootJoint(Ref& skinRef) return parentNodeRef; } -/*void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref& meshRef, Ref& bufferRef, Ref& skinRef, std::vector& inverseBindMatricesData) +void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref& meshRef, Ref& bufferRef, Ref& skinRef, std::vector& inverseBindMatricesData) { if (aimesh->mNumBones < 1) { return; @@ -558,7 +558,7 @@ Ref FindSkeletonRootJoint(Ref& skinRef) delete[] jointsPerVertex; delete[] vertexWeightData; delete[] vertexJointData; -}*/ +} void glTF2Exporter::ExportMeshes() { @@ -663,9 +663,9 @@ void glTF2Exporter::ExportMeshes() } /*************** Skins ****************/ - /*if(aim->HasBones()) { + if(aim->HasBones()) { ExportSkin(*mAsset, aim, m, b, skinRef, inverseBindMatricesData); - }*/ + } } //---------------------------------------- @@ -892,7 +892,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, Ref bufferRef = mAsset->buffers.Get(unsigned (0)); @@ -961,7 +961,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, Ref Date: Fri, 1 Sep 2017 17:56:13 -0400 Subject: [PATCH 034/490] store node mesh vs. meshes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit glTF nodes can only hold one mesh. this simply assigns to and check’s a Node’s Mesh --- code/glTF2Asset.h | 2 +- code/glTF2Asset.inl | 9 +-------- code/glTF2AssetWriter.inl | 4 +++- code/glTF2Exporter.cpp | 19 +++++++++---------- code/glTF2Importer.cpp | 21 +++++++-------------- 5 files changed, 21 insertions(+), 34 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 8e3a6dd7a..9facef6a7 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -767,7 +767,7 @@ namespace glTF2 struct Node : public Object { std::vector< Ref > children; - std::vector< Ref > meshes; + Ref mesh; Nullable matrix; Nullable translation; diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 5316b5e26..bebaeba53 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -954,16 +954,9 @@ inline void Node::Read(Value& obj, Asset& r) } if (Value* mesh = FindUInt(obj, "mesh")) { - //unsigned numMeshes = (unsigned)meshes->Size(); - unsigned numMeshes = 1; - - //std::vector meshList; - - this->meshes.reserve(numMeshes); - Ref meshRef = r.meshes.Retrieve((*mesh).GetUint()); - if (meshRef) this->meshes.push_back(meshRef); + if (meshRef) this->mesh = meshRef; } if (Value* camera = FindUInt(obj, "camera")) { diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index 9aa00729c..4a2aa9f39 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -441,7 +441,9 @@ namespace glTF2 { AddRefsVector(obj, "children", n.children, w.mAl); - AddRefsVector(obj, "meshes", n.meshes, w.mAl); + if (n.mesh) { + obj.AddMember("mesh", n.mesh->index, w.mAl); + } AddRefsVector(obj, "skeletons", n.skeletons, w.mAl); diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 164b559fd..7daedd4a3 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -442,16 +442,15 @@ void glTF2Exporter::ExportMaterials() */ bool FindMeshNode(Ref& nodeIn, Ref& meshNode, std::string meshID) { - for (unsigned int i = 0; i < nodeIn->meshes.size(); ++i) { - if (meshID.compare(nodeIn->meshes[i]->id) == 0) { - meshNode = nodeIn; - return true; - } + + if (nodeIn->mesh && meshID.compare(nodeIn->mesh->id) == 0) { + meshNode = nodeIn; + return true; } for (unsigned int i = 0; i < nodeIn->children.size(); ++i) { if(FindMeshNode(nodeIn->children[i], meshNode, meshID)) { - return true; + return true; } } @@ -722,8 +721,8 @@ unsigned int glTF2Exporter::ExportNodeHierarchy(const aiNode* n) CopyValue(n->mTransformation, node->matrix.value); } - for (unsigned int i = 0; i < n->mNumMeshes; ++i) { - node->meshes.push_back(mAsset->meshes.Get(n->mMeshes[i])); + if (n->mNumMeshes > 0) { + node->mesh = mAsset->meshes.Get(n->mMeshes[0]); } for (unsigned int i = 0; i < n->mNumChildren; ++i) { @@ -751,8 +750,8 @@ unsigned int glTF2Exporter::ExportNode(const aiNode* n, Ref& parent) CopyValue(n->mTransformation, node->matrix.value); } - for (unsigned int i = 0; i < n->mNumMeshes; ++i) { - node->meshes.push_back(mAsset->meshes.Get(n->mMeshes[i])); + if (n->mNumMeshes > 0) { + node->mesh = mAsset->meshes.Get(n->mMeshes[0]); } for (unsigned int i = 0; i < n->mNumChildren; ++i) { diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index a2c0911bd..90ae849bd 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -502,22 +502,15 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector& } } - if (!node.meshes.empty()) { - int count = 0; - for (size_t i = 0; i < node.meshes.size(); ++i) { - int idx = node.meshes[i].GetIndex(); - count += meshOffsets[idx + 1] - meshOffsets[idx]; - } - - ainode->mNumMeshes = count; - ainode->mMeshes = new unsigned int[count]; + if (node.mesh) { + ainode->mNumMeshes = 1; + ainode->mMeshes = new unsigned int[1]; int k = 0; - for (size_t i = 0; i < node.meshes.size(); ++i) { - int idx = node.meshes[i].GetIndex(); - for (unsigned int j = meshOffsets[idx]; j < meshOffsets[idx + 1]; ++j, ++k) { - ainode->mMeshes[k] = j; - } + int idx = node.mesh.GetIndex(); + + for (unsigned int j = meshOffsets[idx]; j < meshOffsets[idx + 1]; ++j, ++k) { + ainode->mMeshes[k] = j; } } From 7f01e3f48f0008a43a64d90c94d6c8288de83498 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Fri, 1 Sep 2017 17:56:42 -0400 Subject: [PATCH 035/490] Only export byteStride if not 0 --- code/glTF2AssetWriter.inl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index 4a2aa9f39..69d54d97b 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -97,7 +97,11 @@ namespace glTF2 { { obj.AddMember("bufferView", a.bufferView->index, w.mAl); obj.AddMember("byteOffset", a.byteOffset, w.mAl); - obj.AddMember("byteStride", a.byteStride, w.mAl); + + if (a.byteStride != 0) { + obj.AddMember("byteStride", a.byteStride, w.mAl); + } + obj.AddMember("componentType", int(a.componentType), w.mAl); obj.AddMember("count", a.count, w.mAl); obj.AddMember("type", StringRef(AttribType::ToString(a.type)), w.mAl); From d277995a972dedcccc5258105b543b5b65fa89c9 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Fri, 1 Sep 2017 17:56:48 -0400 Subject: [PATCH 036/490] Formatting --- code/glTF2Asset.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 9facef6a7..875594254 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -777,7 +777,7 @@ namespace glTF2 Ref camera; std::vector< Ref > skeletons; //!< The ID of skeleton nodes. Each of which is the root of a node hierarchy. - Ref skin; //!< The ID of the skin referenced by this node. + Ref skin; //!< The ID of the skin referenced by this node. std::string jointName; //!< Name used when this node is a joint in a skin. Ref parent; //!< This is not part of the glTF specification. Used as a helper. From 63ef19d9ad1b5a6404fb2520209bb7c0f1e05005 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Fri, 1 Sep 2017 17:56:30 -0400 Subject: [PATCH 037/490] Export extensions --- code/glTF2Exporter.cpp | 18 ------------------ code/glTF2Exporter.h | 1 - code/glTF2Importer.cpp | 7 ------- 3 files changed, 26 deletions(-) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 7daedd4a3..9a2c1cd75 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -107,10 +107,6 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai ExportMetadata(); - if (mScene->mRootNode) { - ExportExtensions(mScene->mRootNode); - } - ExportMaterials(); if (mScene->mRootNode) { @@ -789,20 +785,6 @@ void glTF2Exporter::ExportMetadata() asset.generator = buffer; } -void glTF2Exporter::ExportExtensions(const aiNode* n) -{ - aiMetadata* mMetaData = n->mMetaData; - - if (mMetaData != nullptr) { - bool pbrSpecularGlossiness; - - if (mMetaData->Get("extensionsUsed.pbrSpecularGlossiness", pbrSpecularGlossiness)) { - mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = pbrSpecularGlossiness; - } - } - -} - inline void ExtractAnimationData(Asset& mAsset, std::string& animId, Ref& animRef, Ref& buffer, const aiNodeAnim* nodeChannel, float ticksPerSecond) { // Loop over the data and check to see if it exactly matches an existing buffer. diff --git a/code/glTF2Exporter.h b/code/glTF2Exporter.h index 827189f29..4b9fe0ce0 100644 --- a/code/glTF2Exporter.h +++ b/code/glTF2Exporter.h @@ -118,7 +118,6 @@ namespace Assimp void GetMatColor(const aiMaterial* mat, glTF2::vec4& prop, const char* propName, int type, int idx); void GetMatColor(const aiMaterial* mat, glTF2::vec3& prop, const char* propName, int type, int idx); void ExportMetadata(); - void ExportExtensions(const aiNode* n); void ExportMaterials(); void ExportMeshes(); unsigned int ExportNodeHierarchy(const aiNode* n); diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 90ae849bd..36fe8b70e 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -546,13 +546,6 @@ void glTF2Importer::ImportNodes(glTF2::Asset& r) //if (!mScene->mRootNode) { // mScene->mRootNode = new aiNode("EMPTY"); //} - - //initialize mMetaData; - aiMetadata* mMetaData = new aiMetadata(); - - //store used glTF extensions on the root node, for a lack of a better place. - mMetaData->Add("extensionsUsed.pbrSpecularGlossiness", r.extensionsUsed.KHR_materials_pbrSpecularGlossiness); - mScene->mRootNode->mMetaData = mMetaData; } void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset& r) From 2abdbdb55e2801f284d1a8fc2a090e9da28a4557 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Fri, 1 Sep 2017 18:29:30 -0400 Subject: [PATCH 038/490] Fix unused CopyValue --- code/glTF2Importer.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 36fe8b70e..08145f5fe 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -136,10 +136,10 @@ static void CopyValue(const glTF2::vec4& v, aiColor4D& out) out.r = v[0]; out.g = v[1]; out.b = v[2]; out.a = v[3]; } -static void CopyValue(const glTF2::vec4& v, aiColor3D& out) +/*static void CopyValue(const glTF2::vec4& v, aiColor3D& out) { out.r = v[0]; out.g = v[1]; out.b = v[2]; -} +}*/ static void CopyValue(const glTF2::vec3& v, aiColor4D& out) { @@ -173,14 +173,9 @@ inline void SetMaterialColorProperty(Asset& r, vec4& prop, aiMaterial* mat, cons inline void SetMaterialColorProperty(Asset& r, vec3& prop, aiMaterial* mat, const char* pKey, unsigned int type, unsigned int idx) { - vec4 prop4; - - prop4[0] = prop[0]; - prop4[1] = prop[1]; - prop4[2] = prop[2]; - prop4[3] = 1; - - return SetMaterialColorProperty(r, prop4, mat, pKey, type, idx); + aiColor4D col; + CopyValue(prop, col); + mat->AddProperty(&col, 1, pKey, type, idx); } inline void SetMaterialTextureProperty(std::vector& embeddedTexIdxs, Asset& r, glTF2::TextureInfo prop, aiMaterial* mat, aiTextureType texType, unsigned int texSlot = 0) From 7245cceead0ea647a61cebafc32191e2f588c7ea Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Sun, 3 Sep 2017 22:03:02 -0400 Subject: [PATCH 039/490] Set default values on Sampler --- code/glTF2Asset.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 875594254..711e8231b 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -800,7 +800,7 @@ namespace glTF2 SamplerWrap wrapS; //!< The texture wrapping in the S direction. (required) SamplerWrap wrapT; //!< The texture wrapping in the T direction. (required) - Sampler() {} + Sampler() { SetDefaults(); } void Read(Value& obj, Asset& r); void SetDefaults(); }; From a9c4fa84b5f48fccfc099d8c300144d5ba1807eb Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Sun, 3 Sep 2017 22:09:48 -0400 Subject: [PATCH 040/490] Sampler improvements; Add new LazyDict method Samplers are now imported into assimp internal format from textures; Samplers have defaults as per spec; Sampler enums are strongly typed with UNSET values; Samplers are exported properly; Sampler filters are exported as well; Samplers are re-used across textures on export Default sampler values are not written --- code/glTF2Asset.h | 25 ++++++----- code/glTF2Asset.inl | 28 +++++++----- code/glTF2AssetWriter.inl | 23 ++++++---- code/glTF2Exporter.cpp | 91 +++++++++++++++++++++++---------------- code/glTF2Exporter.h | 2 +- code/glTF2Importer.cpp | 21 +++++++++ 6 files changed, 123 insertions(+), 67 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 711e8231b..aabd462d8 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -242,15 +242,17 @@ namespace glTF2 }; //! Values for the Sampler::magFilter field - enum SamplerMagFilter + enum class SamplerMagFilter: unsigned int { + UNSET = 0, SamplerMagFilter_Nearest = 9728, SamplerMagFilter_Linear = 9729 }; //! Values for the Sampler::minFilter field - enum SamplerMinFilter + enum class SamplerMinFilter: unsigned int { + UNSET = 0, SamplerMinFilter_Nearest = 9728, SamplerMinFilter_Linear = 9729, SamplerMinFilter_Nearest_Mipmap_Nearest = 9984, @@ -260,11 +262,12 @@ namespace glTF2 }; //! Values for the Sampler::wrapS and Sampler::wrapT field - enum SamplerWrap + enum class SamplerWrap: unsigned int { - SamplerWrap_Clamp_To_Edge = 33071, - SamplerWrap_Mirrored_Repeat = 33648, - SamplerWrap_Repeat = 10497 + UNSET = 0, + Clamp_To_Edge = 33071, + Mirrored_Repeat = 33648, + Repeat = 10497 }; //! Values for the Texture::format and Texture::internalFormat fields @@ -795,10 +798,10 @@ namespace glTF2 struct Sampler : public Object { - SamplerMagFilter magFilter; //!< The texture magnification filter. (required) - SamplerMinFilter minFilter; //!< The texture minification filter. (required) - SamplerWrap wrapS; //!< The texture wrapping in the S direction. (required) - SamplerWrap wrapT; //!< The texture wrapping in the T direction. (required) + SamplerMagFilter magFilter; //!< The texture magnification filter. + SamplerMinFilter minFilter; //!< The texture minification filter. + SamplerWrap wrapS; //!< The texture wrapping in the S direction. + SamplerWrap wrapT; //!< The texture wrapping in the T direction. Sampler() { SetDefaults(); } void Read(Value& obj, Asset& r); @@ -955,6 +958,8 @@ namespace glTF2 Ref Get(unsigned int i); Ref Get(const char* id); + bool Has(const char* id); + Ref Create(const char* id); Ref Create(const std::string& id) { return Create(id.c_str()); } diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index bebaeba53..5764c6ff6 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -244,7 +244,17 @@ Ref LazyDict::Get(const char* id) return Ref(mObjs, it->second); } - throw std::out_of_range("id \"" + std::string(id) + "\" Doesn't exist"); + return Create(id); +} + +template +bool LazyDict::Has(const char* id) +{ + id = T::TranslateId(mAsset, id); + + typename IdDict::iterator it = mObjsById.find(id); + + return it != mObjsById.end(); } template @@ -697,6 +707,7 @@ inline void Sampler::Read(Value& obj, Asset& r) { SetDefaults(); + ReadMember(obj, "name", name); ReadMember(obj, "magFilter", magFilter); ReadMember(obj, "minFilter", minFilter); ReadMember(obj, "wrapS", wrapS); @@ -705,10 +716,11 @@ inline void Sampler::Read(Value& obj, Asset& r) inline void Sampler::SetDefaults() { - magFilter = SamplerMagFilter_Linear; - minFilter = SamplerMinFilter_Linear; - wrapS = SamplerWrap_Repeat; - wrapT = SamplerWrap_Repeat; + //only wrapping modes have defaults + wrapS = SamplerWrap::Repeat; + wrapT = SamplerWrap::Repeat; + magFilter = SamplerMagFilter::UNSET; + minFilter = SamplerMinFilter::UNSET; } inline void Texture::Read(Value& obj, Asset& r) @@ -883,7 +895,6 @@ inline void Mesh::Read(Value& pJSON_Object, Asset& pAsset_Root) // Valid attribute semantics include POSITION, NORMAL, TEXCOORD, COLOR, JOINT, JOINTMATRIX, // and WEIGHT.Attribute semantics can be of the form[semantic]_[set_index], e.g., TEXCOORD_0, TEXCOORD_1, etc. - //@TODO: update this int undPos = 0; Mesh::AccessorList* vec = 0; if (GetAttribVector(prim, attr, vec, undPos)) { @@ -943,7 +954,6 @@ inline void Node::Read(Value& obj, Asset& r) } } - if (Value* matrix = FindArray(obj, "matrix")) { ReadValue(*matrix, this->matrix); } @@ -978,7 +988,6 @@ inline void Scene::Read(Value& obj, Asset& r) } } - inline void AssetMetadata::Read(Document& doc) { // read the version, etc. @@ -1013,8 +1022,6 @@ inline void AssetMetadata::Read(Document& doc) } } - - // // Asset methods implementation // @@ -1142,7 +1149,6 @@ inline void Asset::SetAsBinary() } } - inline void Asset::ReadExtensionsUsed(Document& doc) { Value* extsUsed = FindArray(doc, "extensionsUsed"); diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index 69d54d97b..f7943172e 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -467,17 +467,24 @@ namespace glTF2 { inline void Write(Value& obj, Sampler& b, AssetWriter& w) { - if (b.wrapS) { - obj.AddMember("wrapS", b.wrapS, w.mAl); + if (!b.name.empty()) { + obj.AddMember("name", b.name, w.mAl); } - if (b.wrapT) { - obj.AddMember("wrapT", b.wrapT, w.mAl); + + if (b.wrapS != SamplerWrap::UNSET && b.wrapS != SamplerWrap::Repeat) { + obj.AddMember("wrapS", static_cast(b.wrapS), w.mAl); } - if (b.magFilter) { - obj.AddMember("magFilter", b.magFilter, w.mAl); + + if (b.wrapT != SamplerWrap::UNSET && b.wrapT != SamplerWrap::Repeat) { + obj.AddMember("wrapT", static_cast(b.wrapT), w.mAl); } - if (b.minFilter) { - obj.AddMember("minFilter", b.minFilter, w.mAl); + + if (b.magFilter != SamplerMagFilter::UNSET) { + obj.AddMember("magFilter", static_cast(b.magFilter), w.mAl); + } + + if (b.minFilter != SamplerMinFilter::UNSET) { + obj.AddMember("minFilter", static_cast(b.minFilter), w.mAl); } } diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 9a2c1cd75..fec7236e3 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -223,50 +223,63 @@ inline Ref ExportData(Asset& a, std::string& meshName, Ref& bu return acc; } -void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture) +inline void SetSamplerWrap(SamplerWrap& wrap, aiTextureMapMode map) { - std::string samplerId = mAsset->FindUniqueID("", "sampler"); - texture->sampler = mAsset->samplers.Create(samplerId); - - aiTextureMapMode mapU, mapV; - aiGetMaterialInteger(mat,AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0),(int*)&mapU); - aiGetMaterialInteger(mat,AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0),(int*)&mapV); - - switch (mapU) { - case aiTextureMapMode_Wrap: - texture->sampler->wrapS = SamplerWrap_Repeat; - break; + switch (map) { case aiTextureMapMode_Clamp: - texture->sampler->wrapS = SamplerWrap_Clamp_To_Edge; + wrap = SamplerWrap::Clamp_To_Edge; break; case aiTextureMapMode_Mirror: - texture->sampler->wrapS = SamplerWrap_Mirrored_Repeat; + wrap = SamplerWrap::Mirrored_Repeat; break; - case aiTextureMapMode_Decal: - default: - texture->sampler->wrapS = SamplerWrap_Repeat; - break; - }; - - switch (mapV) { case aiTextureMapMode_Wrap: - texture->sampler->wrapT = SamplerWrap_Repeat; - break; - case aiTextureMapMode_Clamp: - texture->sampler->wrapT = SamplerWrap_Clamp_To_Edge; - break; - case aiTextureMapMode_Mirror: - texture->sampler->wrapT = SamplerWrap_Mirrored_Repeat; - break; case aiTextureMapMode_Decal: default: - texture->sampler->wrapT = SamplerWrap_Repeat; + wrap = SamplerWrap::Repeat; break; }; +} - // Hard coded Texture filtering options because I do not know where to find them in the aiMaterial. - texture->sampler->magFilter = SamplerMagFilter_Linear; - texture->sampler->minFilter = SamplerMinFilter_Linear_Mipmap_Linear; +void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture, aiTextureType tt, unsigned int slot) +{ + aiString aId; + std::string id; + if (aiGetMaterialString(mat, (std::string(_AI_MATKEY_MAPPING_BASE) + "id").c_str(), tt, slot, &aId) == AI_SUCCESS) { + id = aId.C_Str(); + } + + if (mAsset->samplers.Has(id.c_str())) { + texture->sampler = mAsset->samplers.Get(id.c_str()); + } else { + id = mAsset->FindUniqueID(id, "sampler"); + + texture->sampler = mAsset->samplers.Create(id.c_str()); + + aiTextureMapMode mapU, mapV; + SamplerMagFilter filterMag; + SamplerMinFilter filterMin; + + if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_U(tt, slot), (int*)&mapU) == AI_SUCCESS) { + SetSamplerWrap(texture->sampler->wrapS, mapU); + } + + if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_V(tt, slot), (int*)&mapV) == AI_SUCCESS) { + SetSamplerWrap(texture->sampler->wrapT, mapV); + } + + if (aiGetMaterialInteger(mat, (std::string(_AI_MATKEY_MAPPING_BASE) + "filtermag").c_str(), tt, slot, (int*)&filterMag) == AI_SUCCESS) { + texture->sampler->magFilter = filterMag; + } + + if (aiGetMaterialInteger(mat, (std::string(_AI_MATKEY_MAPPING_BASE) + "filtermin").c_str(), tt, slot, (int*)&filterMin) == AI_SUCCESS) { + texture->sampler->minFilter = filterMin; + } + + aiString name; + if (aiGetMaterialString(mat, (std::string(_AI_MATKEY_MAPPING_BASE) + "name").c_str(), tt, slot, &name) == AI_SUCCESS) { + texture->sampler->name = name.C_Str(); + } + } } void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int slot) @@ -324,7 +337,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe texture->source->uri = path; } - GetTexSampler(mat, texture); + GetTexSampler(mat, texture, tt, slot); } } } @@ -390,17 +403,23 @@ void glTF2Exporter::ExportMaterials() for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) { const aiMaterial* mat = mScene->mMaterials[i]; + std::string id = "material_" + std::to_string(i); + + Ref m = mAsset->materials.Create(id); + std::string name; if (mat->Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) { name = aiName.C_Str(); } name = mAsset->FindUniqueID(name, "material"); - Ref m = mAsset->materials.Create(name); + m->name = name; GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_DIFFUSE); GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, aiTextureType_UNKNOWN, 0);//get unknown slot GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); + mat->Get("$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0, m->pbrMetallicRoughness.metallicFactor); + mat->Get("$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0, m->pbrMetallicRoughness.roughnessFactor); GetMatTex(mat, m->normalTexture, aiTextureType_NORMALS); GetMatTex(mat, m->occlusionTexture, aiTextureType_LIGHTMAP); @@ -409,8 +428,6 @@ void glTF2Exporter::ExportMaterials() mat->Get(AI_MATKEY_TWOSIDED, m->doubleSided); mat->Get("$mat.gltf.alphaCutoff", 0, 0, m->alphaCutoff); - mat->Get("$mat.gltf.metallicFactor", 0, 0, m->pbrMetallicRoughness.metallicFactor); - mat->Get("$mat.gltf.roughnessFactor", 0, 0, m->pbrMetallicRoughness.roughnessFactor); mat->Get("$mat.gltf.alphaMode", 0, 0, m->alphaMode); bool hasPbrSpecularGlossiness; diff --git a/code/glTF2Exporter.h b/code/glTF2Exporter.h index 4b9fe0ce0..420c2afa6 100644 --- a/code/glTF2Exporter.h +++ b/code/glTF2Exporter.h @@ -108,7 +108,7 @@ namespace Assimp void WriteBinaryData(IOStream* outfile, std::size_t sceneLength); - void GetTexSampler(const aiMaterial* mat, glTF2::Ref texture); + void GetTexSampler(const aiMaterial* mat, glTF2::Ref texture, aiTextureType tt, unsigned int slot); void GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx); void GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx); void GetMatTex(const aiMaterial* mat, glTF2::Ref& texture, aiTextureType tt, unsigned int slot); diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 08145f5fe..86301a3f4 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -194,6 +194,27 @@ inline void SetMaterialTextureProperty(std::vector& embeddedTexIdxs, Asset& const char *texCoordName = (std::string(_AI_MATKEY_TEXTURE_BASE) + ".texCoord").c_str(); mat->AddProperty(&prop.texCoord, 1, texCoordName, texType, texSlot); + + if (prop.texture->sampler) { + Ref sampler = prop.texture->sampler; + + aiString name(sampler->name); + aiString id(sampler->id); + + mat->AddProperty(&name, (std::string(_AI_MATKEY_MAPPING_BASE) + "name").c_str(), texType, texSlot); + mat->AddProperty(&id, (std::string(_AI_MATKEY_MAPPING_BASE) + "id").c_str(), texType, texSlot); + + mat->AddProperty(&sampler->wrapS, 1, AI_MATKEY_MAPPINGMODE_U(texType, texSlot)); + mat->AddProperty(&sampler->wrapT, 1, AI_MATKEY_MAPPINGMODE_V(texType, texSlot)); + + if (sampler->magFilter != SamplerMagFilter::UNSET) { + mat->AddProperty(&sampler->magFilter, 1, (std::string(_AI_MATKEY_MAPPING_BASE) + "filtermag").c_str(), texType, texSlot); + } + + if (sampler->minFilter != SamplerMinFilter::UNSET) { + mat->AddProperty(&sampler->minFilter, 1, (std::string(_AI_MATKEY_MAPPING_BASE) + "filtermin").c_str(), texType, texSlot); + } + } } } From 37527849b771552c9003dc488a5a97d76a196123 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Sun, 3 Sep 2017 22:11:20 -0400 Subject: [PATCH 041/490] Export material names properly --- code/glTF2AssetWriter.inl | 4 ---- code/glTF2Importer.cpp | 8 +++++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index f7943172e..c10a6a08b 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -284,10 +284,6 @@ namespace glTF2 { inline void Write(Value& obj, Material& m, AssetWriter& w) { - if (!m.name.empty()) { - obj.AddMember("name", m.name, w.mAl); - } - Value pbrMetallicRoughness; pbrMetallicRoughness.SetObject(); { diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 86301a3f4..82ea5f3e7 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -125,7 +125,6 @@ bool glTF2Importer::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool } - //static void CopyValue(const glTF2::vec3& v, aiColor3D& out) //{ // out.r = v[0]; out.g = v[1]; out.b = v[2]; @@ -228,8 +227,11 @@ void glTF2Importer::ImportMaterials(glTF2::Asset& r) Material& mat = r.materials[i]; - aiString str(mat.id); - aimat->AddProperty(&str, AI_MATKEY_NAME); + if (!mat.name.empty()) { + aiString str(mat.name); + + aimat->AddProperty(&str, AI_MATKEY_NAME); + } SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE); From 21259e08357c0b8487ab33ffd87d5f9118420dd7 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Tue, 5 Sep 2017 14:39:52 -0400 Subject: [PATCH 042/490] Use different form of index accessor --- code/glTF2Asset.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 5764c6ff6..88dbfcfdb 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -211,7 +211,7 @@ Ref LazyDict::Retrieve(unsigned int i) throw DeadlyImportError("GLTF: Field is not an array \"" + std::string(mDictId) + "\""); } - Value& obj = mDict->operator[](i); + Value &obj = (*mDict)[i]; if (!obj.IsObject()) { throw DeadlyImportError("GLTF: Object at index \"" + std::to_string(i) + "\" is not a JSON object"); From 1a5823700f17caa6c6cdedfd34ce5bbd3ba85556 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Tue, 5 Sep 2017 14:40:27 -0400 Subject: [PATCH 043/490] Remove need for Has by returning an empty Ref in Get --- code/glTF2Asset.h | 2 -- code/glTF2Asset.inl | 12 +----------- code/glTF2Exporter.cpp | 4 ++-- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index aabd462d8..9a70ff4ff 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -958,8 +958,6 @@ namespace glTF2 Ref Get(unsigned int i); Ref Get(const char* id); - bool Has(const char* id); - Ref Create(const char* id); Ref Create(const std::string& id) { return Create(id.c_str()); } diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 88dbfcfdb..1c75d0040 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -244,17 +244,7 @@ Ref LazyDict::Get(const char* id) return Ref(mObjs, it->second); } - return Create(id); -} - -template -bool LazyDict::Has(const char* id) -{ - id = T::TranslateId(mAsset, id); - - typename IdDict::iterator it = mObjsById.find(id); - - return it != mObjsById.end(); + return Ref(); } template diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index fec7236e3..073309b06 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -248,8 +248,8 @@ void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture, a id = aId.C_Str(); } - if (mAsset->samplers.Has(id.c_str())) { - texture->sampler = mAsset->samplers.Get(id.c_str()); + if (Ref ref = mAsset->samplers.Get(id.c_str())) { + texture->sampler = ref; } else { id = mAsset->FindUniqueID(id, "sampler"); From 03cfa04ee4a4ef85a3eeefa879bec4e088418287 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Tue, 5 Sep 2017 15:45:32 -0400 Subject: [PATCH 044/490] Define default material values as static constants for reuse --- code/glTF2Asset.h | 10 +++++++--- code/glTF2Asset.inl | 16 ++++++++-------- code/glTF2AssetWriter.inl | 14 ++------------ 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 9a70ff4ff..486e23bc0 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -135,8 +135,7 @@ namespace glTF2 // Vec/matrix types, as raw float arrays typedef float (vec3)[3]; typedef float (vec4)[4]; - typedef float (mat4)[16]; - + typedef float (mat4)[16]; namespace Util { @@ -670,7 +669,12 @@ namespace glTF2 inline uint8_t* StealData(); inline void SetData(uint8_t* data, size_t length, Asset& r); - }; + }; + + const vec4 defaultBaseColor = {1, 1, 1, 1}; + const vec3 defaultEmissiveFactor = {0, 0, 0}; + const vec4 defaultDiffuseFactor = {1, 1, 1, 1}; + const vec3 defaultSpecularFactor = {1, 1, 1}; struct TextureInfo { diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 1c75d0040..07e0a5e8e 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -803,28 +803,28 @@ inline void Material::Read(Value& material, Asset& r) } namespace { - void SetVector(vec4& v, float x, float y, float z, float w) - { v[0] = x; v[1] = y; v[2] = z; v[3] = w; } + void SetVector(vec4& v, const float(&in)[4]) + { v[0] = in[0]; v[1] = in[1]; v[2] = in[2]; v[3] = in[3]; } - void SetVector(vec3& v, float x, float y, float z) - { v[0] = x; v[1] = y; v[2] = z; } + void SetVector(vec3& v, const float(&in)[3]) + { v[0] = in[0]; v[1] = in[1]; v[2] = in[2]; } } inline void Material::SetDefaults() { //pbr materials - SetVector(pbrMetallicRoughness.baseColorFactor, 1, 1, 1, 1); + SetVector(pbrMetallicRoughness.baseColorFactor, defaultBaseColor); pbrMetallicRoughness.metallicFactor = 1.0; pbrMetallicRoughness.roughnessFactor = 1.0; - SetVector(emissiveFactor, 0, 0, 0); + SetVector(emissiveFactor, defaultEmissiveFactor); alphaMode = "OPAQUE"; alphaCutoff = 0.5; doubleSided = false; //pbrSpecularGlossiness properties - SetVector(pbrSpecularGlossiness.diffuseFactor, 1, 1, 1, 1); - SetVector(pbrSpecularGlossiness.specularFactor, 1, 1, 1); + SetVector(pbrSpecularGlossiness.diffuseFactor, defaultDiffuseFactor); + SetVector(pbrSpecularGlossiness.specularFactor, defaultSpecularFactor); pbrSpecularGlossiness.glossinessFactor = 1.0; } diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index c10a6a08b..475c7a0d9 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -268,7 +268,7 @@ namespace glTF2 { } template - inline void WriteVec(Value& obj, float(&prop)[N], const char* propName, float(&defaultVal)[N], MemoryPoolAllocator<>& al) + inline void WriteVec(Value& obj, float(&prop)[N], const char* propName, const float(&defaultVal)[N], MemoryPoolAllocator<>& al) { if (!std::equal(std::begin(prop), std::end(prop), std::begin(defaultVal))) { WriteVec(obj, prop, propName, al); @@ -289,10 +289,7 @@ namespace glTF2 { { WriteTex(pbrMetallicRoughness, m.pbrMetallicRoughness.baseColorTexture, "baseColorTexture", w.mAl); WriteTex(pbrMetallicRoughness, m.pbrMetallicRoughness.metallicRoughnessTexture, "metallicRoughnessTexture", w.mAl); - - //@TODO: define this as a constant? - vec4 defaultEmissiveFactor = {1, 1, 1, 1}; - WriteVec(pbrMetallicRoughness, m.pbrMetallicRoughness.baseColorFactor, "baseColorFactor", defaultEmissiveFactor, w.mAl); + WriteVec(pbrMetallicRoughness, m.pbrMetallicRoughness.baseColorFactor, "baseColorFactor", defaultBaseColor, w.mAl); if (m.pbrMetallicRoughness.metallicFactor != 1) { WriteFloat(pbrMetallicRoughness, m.pbrMetallicRoughness.metallicFactor, "metallicFactor", w.mAl); @@ -310,9 +307,6 @@ namespace glTF2 { WriteTex(obj, m.normalTexture, "normalTexture", w.mAl); WriteTex(obj, m.emissiveTexture, "emissiveTexture", w.mAl); WriteTex(obj, m.occlusionTexture, "occlusionTexture", w.mAl); - - //@TODO: define this as a constant? - vec3 defaultEmissiveFactor = {0, 0, 0}; WriteVec(obj, m.emissiveFactor, "emissiveFactor", defaultEmissiveFactor, w.mAl); if (m.alphaCutoff != 0.5) { @@ -335,11 +329,7 @@ namespace glTF2 { pbrSpecularGlossiness.SetObject(); { //pbrSpecularGlossiness - - vec4 defaultDiffuseFactor = {1, 1, 1, 1}; WriteVec(pbrSpecularGlossiness, m.pbrSpecularGlossiness.diffuseFactor, "diffuseFactor", defaultDiffuseFactor, w.mAl); - - vec3 defaultSpecularFactor = {1, 1, 1}; WriteVec(pbrSpecularGlossiness, m.pbrSpecularGlossiness.specularFactor, "specularFactor", defaultSpecularFactor, w.mAl); if (m.pbrSpecularGlossiness.glossinessFactor != 1) { From 44757af34a0047e974ba6d30b9b027d0fffecc5d Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Tue, 5 Sep 2017 16:29:00 -0400 Subject: [PATCH 045/490] Implement pbrSpecularGlossiness property as Nullable --- code/glTF2Asset.h | 8 +++++--- code/glTF2Asset.inl | 23 ++++++++++++++--------- code/glTF2AssetWriter.inl | 21 +++++++++++---------- code/glTF2Exporter.cpp | 15 +++++++++------ code/glTF2Importer.cpp | 14 +++++++------- 5 files changed, 46 insertions(+), 35 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 486e23bc0..07f8614ab 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -711,12 +711,14 @@ namespace glTF2 struct PbrSpecularGlossiness { - bool on = false; vec4 diffuseFactor; vec3 specularFactor; float glossinessFactor; TextureInfo diffuseTexture; - TextureInfo specularGlossinessTexture; + TextureInfo specularGlossinessTexture; + + PbrSpecularGlossiness() { SetDefaults(); } + void SetDefaults(); }; //! The material appearance of a primitive. @@ -735,7 +737,7 @@ namespace glTF2 bool doubleSided; //extension: KHR_materials_pbrSpecularGlossiness - PbrSpecularGlossiness pbrSpecularGlossiness; + Nullable pbrSpecularGlossiness; Material() { SetDefaults(); } void Read(Value& obj, Asset& r); diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 07e0a5e8e..e2d812563 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -790,13 +790,15 @@ inline void Material::Read(Value& material, Asset& r) if (Value* extensions = FindObject(material, "extensions")) { if (r.extensionsUsed.KHR_materials_pbrSpecularGlossiness) { if (Value* pbrSpecularGlossiness = FindObject(*extensions, "KHR_materials_pbrSpecularGlossiness")) { - this->pbrSpecularGlossiness.on = true; + PbrSpecularGlossiness pbrSG; - ReadMember(*pbrSpecularGlossiness, "diffuseFactor", this->pbrSpecularGlossiness.diffuseFactor); - ReadTextureProperty(r, *pbrSpecularGlossiness, "diffuseTexture", this->pbrSpecularGlossiness.diffuseTexture); - ReadTextureProperty(r, *pbrSpecularGlossiness, "specularGlossinessTexture", this->pbrSpecularGlossiness.specularGlossinessTexture); - ReadMember(*pbrSpecularGlossiness, "specularFactor", this->pbrSpecularGlossiness.specularFactor); - ReadMember(*pbrSpecularGlossiness, "glossinessFactor", this->pbrSpecularGlossiness.glossinessFactor); + ReadMember(*pbrSpecularGlossiness, "diffuseFactor", pbrSG.diffuseFactor); + ReadTextureProperty(r, *pbrSpecularGlossiness, "diffuseTexture", pbrSG.diffuseTexture); + ReadTextureProperty(r, *pbrSpecularGlossiness, "specularGlossinessTexture", pbrSG.specularGlossinessTexture); + ReadMember(*pbrSpecularGlossiness, "specularFactor", pbrSG.specularFactor); + ReadMember(*pbrSpecularGlossiness, "glossinessFactor", pbrSG.glossinessFactor); + + this->pbrSpecularGlossiness = Nullable(pbrSG); } } } @@ -821,11 +823,14 @@ inline void Material::SetDefaults() alphaMode = "OPAQUE"; alphaCutoff = 0.5; doubleSided = false; +} +inline void PbrSpecularGlossiness::SetDefaults() +{ //pbrSpecularGlossiness properties - SetVector(pbrSpecularGlossiness.diffuseFactor, defaultDiffuseFactor); - SetVector(pbrSpecularGlossiness.specularFactor, defaultSpecularFactor); - pbrSpecularGlossiness.glossinessFactor = 1.0; + SetVector(diffuseFactor, defaultDiffuseFactor); + SetVector(specularFactor, defaultSpecularFactor); + glossinessFactor = 1.0; } namespace { diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index 475c7a0d9..ad68a14e1 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -324,22 +324,23 @@ namespace glTF2 { Value exts; exts.SetObject(); - if (m.pbrSpecularGlossiness.on) { + if (m.pbrSpecularGlossiness.isPresent) { Value pbrSpecularGlossiness; pbrSpecularGlossiness.SetObject(); - { - //pbrSpecularGlossiness - WriteVec(pbrSpecularGlossiness, m.pbrSpecularGlossiness.diffuseFactor, "diffuseFactor", defaultDiffuseFactor, w.mAl); - WriteVec(pbrSpecularGlossiness, m.pbrSpecularGlossiness.specularFactor, "specularFactor", defaultSpecularFactor, w.mAl); - if (m.pbrSpecularGlossiness.glossinessFactor != 1) { - WriteFloat(obj, m.pbrSpecularGlossiness.glossinessFactor, "glossinessFactor", w.mAl); - } + PbrSpecularGlossiness &pbrSG = m.pbrSpecularGlossiness.value; - WriteTex(pbrSpecularGlossiness, m.pbrSpecularGlossiness.diffuseTexture, "diffuseTexture", w.mAl); - WriteTex(pbrSpecularGlossiness, m.pbrSpecularGlossiness.specularGlossinessTexture, "specularGlossinessTexture", w.mAl); + //pbrSpecularGlossiness + WriteVec(pbrSpecularGlossiness, pbrSG.diffuseFactor, "diffuseFactor", defaultDiffuseFactor, w.mAl); + WriteVec(pbrSpecularGlossiness, pbrSG.specularFactor, "specularFactor", defaultSpecularFactor, w.mAl); + + if (pbrSG.glossinessFactor != 1) { + WriteFloat(obj, pbrSG.glossinessFactor, "glossinessFactor", w.mAl); } + WriteTex(pbrSpecularGlossiness, pbrSG.diffuseTexture, "diffuseTexture", w.mAl); + WriteTex(pbrSpecularGlossiness, pbrSG.specularGlossinessTexture, "specularGlossinessTexture", w.mAl); + if (!pbrSpecularGlossiness.ObjectEmpty()) { exts.AddMember("KHR_materials_pbrSpecularGlossiness", pbrSpecularGlossiness, w.mAl); } diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 073309b06..600e2b3d4 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -439,12 +439,15 @@ void glTF2Exporter::ExportMaterials() KHR_materials_pbrSpecularGlossiness = true; } - m->pbrSpecularGlossiness.on = true; - GetMatColor(mat, m->pbrSpecularGlossiness.diffuseFactor, "$clr.diffuse", 0, 1); - GetMatColor(mat, m->pbrSpecularGlossiness.specularFactor, "$clr.specular", 0, 1); - mat->Get("$mat.gltf.glossinessFactor", 0, 0, m->pbrSpecularGlossiness.glossinessFactor); - GetMatTex(mat, m->pbrSpecularGlossiness.diffuseTexture, aiTextureType_DIFFUSE, 1); - GetMatTex(mat, m->pbrSpecularGlossiness.specularGlossinessTexture, aiTextureType_UNKNOWN, 1); + PbrSpecularGlossiness pbrSG; + + GetMatColor(mat, pbrSG.diffuseFactor, "$clr.diffuse", 0, 1); + GetMatColor(mat, pbrSG.specularFactor, "$clr.specular", 0, 1); + mat->Get("$mat.gltf.glossinessFactor", 0, 0, pbrSG.glossinessFactor); + GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE, 1); + GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_UNKNOWN, 1); + + m->pbrSpecularGlossiness = Nullable(pbrSG); } } } diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 82ea5f3e7..ff806257f 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -249,13 +249,13 @@ void glTF2Importer::ImportMaterials(glTF2::Asset& r) aimat->AddProperty(&mat.alphaCutoff, 1, "$mat.gltf.alphaCutoff"); //pbrSpecularGlossiness - if (mat.pbrSpecularGlossiness.on) { - aimat->AddProperty(&mat.pbrSpecularGlossiness.on, 1, "$mat.gltf.pbrSpecularGlossiness.on"); - SetMaterialColorProperty(r, mat.pbrSpecularGlossiness.diffuseFactor, aimat, "$clr.diffuse", 0, 1); - SetMaterialColorProperty(r, mat.pbrSpecularGlossiness.specularFactor, aimat, "$clr.specular", 0, 1); - aimat->AddProperty(&mat.pbrSpecularGlossiness.glossinessFactor, 1, "$mat.gltf.pbrSpecularGlossiness.glossinessFactor"); - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrSpecularGlossiness.diffuseTexture, aimat, aiTextureType_DIFFUSE, 1); - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrSpecularGlossiness.specularGlossinessTexture, aimat, aiTextureType_UNKNOWN, 1); + if (mat.pbrSpecularGlossiness.isPresent) { + aimat->AddProperty(&mat.pbrSpecularGlossiness.isPresent, 1, "$mat.gltf.pbrSpecularGlossiness.on"); + SetMaterialColorProperty(r, mat.pbrSpecularGlossiness.value.diffuseFactor, aimat, "$clr.diffuse", 0, 1); + SetMaterialColorProperty(r, mat.pbrSpecularGlossiness.value.specularFactor, aimat, "$clr.specular", 0, 1); + aimat->AddProperty(&mat.pbrSpecularGlossiness.value.glossinessFactor, 1, "$mat.gltf.pbrSpecularGlossiness.glossinessFactor"); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrSpecularGlossiness.value.diffuseTexture, aimat, aiTextureType_DIFFUSE, 1); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrSpecularGlossiness.value.specularGlossinessTexture, aimat, aiTextureType_UNKNOWN, 1); } } } From 54dd4804cda6f5f6a6e08781ee4e42325f12755a Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Tue, 5 Sep 2017 16:39:34 -0400 Subject: [PATCH 046/490] Fix indentation --- code/glTF2Importer.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index ff806257f..742088042 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -306,7 +306,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r) for (unsigned int m = 0; m < r.meshes.Size(); ++m) { Mesh& mesh = r.meshes[m]; - meshOffsets.push_back(k); + meshOffsets.push_back(k); k += unsigned(mesh.primitives.size()); for (unsigned int p = 0; p < mesh.primitives.size(); ++p) { @@ -339,16 +339,17 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r) case PrimitiveMode_TRIANGLE_FAN: aim->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; break; + } Mesh::Primitive::Attributes& attr = prim.attributes; - if (attr.position.size() > 0 && attr.position[0]) { + if (attr.position.size() > 0 && attr.position[0]) { aim->mNumVertices = attr.position[0]->count; attr.position[0]->ExtractData(aim->mVertices); - } + } - if (attr.normal.size() > 0 && attr.normal[0]) attr.normal[0]->ExtractData(aim->mNormals); + if (attr.normal.size() > 0 && attr.normal[0]) attr.normal[0]->ExtractData(aim->mNormals); for (size_t tc = 0; tc < attr.texcoord.size() && tc < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++tc) { attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc]); @@ -362,7 +363,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r) if (prim.indices) { - aiFace* faces = 0; + aiFace* faces = 0; unsigned int nFaces = 0; unsigned int count = prim.indices->count; @@ -638,7 +639,7 @@ void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IO // TODO: it does not split the loaded vertices, should it? //pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; - MakeVerboseFormatProcess process; + MakeVerboseFormatProcess process; process.Execute(pScene); From 3ba00ca4219ea12fdcab9f20c32720cb533b26b2 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Tue, 5 Sep 2017 18:04:09 -0400 Subject: [PATCH 047/490] Define gltf material property names as constants --- code/glTF2Asset.h | 26 +++++++++++++++++++++++++- code/glTF2Exporter.cpp | 30 +++++++++++++++--------------- code/glTF2Importer.cpp | 36 ++++++++++++++++++------------------ 3 files changed, 58 insertions(+), 34 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 07f8614ab..b4369fe71 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -163,7 +163,31 @@ namespace glTF2 //! Magic number for GLB files - #define AI_GLB_MAGIC_NUMBER "glTF" + #define AI_GLB_MAGIC_NUMBER "glTF" + + #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0,0 + #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 + #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 0 + #define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0 + #define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0 + #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 + #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_FACTOR "$clr.diffuse", 0, 1 + #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULAR_FACTOR "$clr.specular", 0, 1 + #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0,0 + #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_TEXTURE aiTextureType_DIFFUSE, 1 + #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULARGLOSSINESS_TEXTURE aiTextureType_UNKNOWN, 1 + + #define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE (std::string(_AI_MATKEY_TEXTURE_BASE) + ".texCoord").c_str() + #define _AI_MATKEY_GLTF_MAPPINGNAME_BASE (std::string(_AI_MATKEY_MAPPING_BASE) + "name").c_str() + #define _AI_MATKEY_GLTF_MAPPINGID_BASE (std::string(_AI_MATKEY_MAPPING_BASE) + "id").c_str() + #define _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE (std::string(_AI_MATKEY_MAPPING_BASE) + "filtermag").c_str() + #define _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE (std::string(_AI_MATKEY_MAPPING_BASE) + "filtermin").c_str() + + #define AI_MATKEY_GLTF_TEXTURE_TEXCOORD _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N + #define AI_MATKEY_GLTF_MAPPINGNAME(type, N) _AI_MATKEY_GLTF_MAPPINGNAME_BASE, type, N + #define AI_MATKEY_GLTF_MAPPINGID(type, N) _AI_MATKEY_GLTF_MAPPINGID_BASE, type, N + #define AI_MATKEY_GLTF_MAPPINGFILTER_MAG(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE, type, N + #define AI_MATKEY_GLTF_MAPPINGFILTER_MIN(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE, type, N #ifdef ASSIMP_API #include "./../include/assimp/Compiler/pushpack1.h" diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 600e2b3d4..931e491e3 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -244,7 +244,7 @@ void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture, a { aiString aId; std::string id; - if (aiGetMaterialString(mat, (std::string(_AI_MATKEY_MAPPING_BASE) + "id").c_str(), tt, slot, &aId) == AI_SUCCESS) { + if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGID(tt, slot), &aId) == AI_SUCCESS) { id = aId.C_Str(); } @@ -267,16 +267,16 @@ void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture, a SetSamplerWrap(texture->sampler->wrapT, mapV); } - if (aiGetMaterialInteger(mat, (std::string(_AI_MATKEY_MAPPING_BASE) + "filtermag").c_str(), tt, slot, (int*)&filterMag) == AI_SUCCESS) { + if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(tt, slot), (int*)&filterMag) == AI_SUCCESS) { texture->sampler->magFilter = filterMag; } - if (aiGetMaterialInteger(mat, (std::string(_AI_MATKEY_MAPPING_BASE) + "filtermin").c_str(), tt, slot, (int*)&filterMin) == AI_SUCCESS) { + if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(tt, slot), (int*)&filterMin) == AI_SUCCESS) { texture->sampler->minFilter = filterMin; } aiString name; - if (aiGetMaterialString(mat, (std::string(_AI_MATKEY_MAPPING_BASE) + "name").c_str(), tt, slot, &name) == AI_SUCCESS) { + if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGNAME(tt, slot), &name) == AI_SUCCESS) { texture->sampler->name = name.C_Str(); } } @@ -416,10 +416,10 @@ void glTF2Exporter::ExportMaterials() m->name = name; GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_DIFFUSE); - GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, aiTextureType_UNKNOWN, 0);//get unknown slot + GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); - mat->Get("$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0, m->pbrMetallicRoughness.metallicFactor); - mat->Get("$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0, m->pbrMetallicRoughness.roughnessFactor); + mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor); + mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor); GetMatTex(mat, m->normalTexture, aiTextureType_NORMALS); GetMatTex(mat, m->occlusionTexture, aiTextureType_LIGHTMAP); @@ -427,11 +427,11 @@ void glTF2Exporter::ExportMaterials() GetMatColor(mat, m->emissiveFactor, AI_MATKEY_COLOR_EMISSIVE); mat->Get(AI_MATKEY_TWOSIDED, m->doubleSided); - mat->Get("$mat.gltf.alphaCutoff", 0, 0, m->alphaCutoff); - mat->Get("$mat.gltf.alphaMode", 0, 0, m->alphaMode); + mat->Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff); + mat->Get(AI_MATKEY_GLTF_ALPHAMODE, m->alphaMode); bool hasPbrSpecularGlossiness; - mat->Get("$mat.gltf.pbrSpecularGlossiness.on", 0, 0, hasPbrSpecularGlossiness); + mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS, hasPbrSpecularGlossiness); if (hasPbrSpecularGlossiness) { @@ -441,11 +441,11 @@ void glTF2Exporter::ExportMaterials() PbrSpecularGlossiness pbrSG; - GetMatColor(mat, pbrSG.diffuseFactor, "$clr.diffuse", 0, 1); - GetMatColor(mat, pbrSG.specularFactor, "$clr.specular", 0, 1); - mat->Get("$mat.gltf.glossinessFactor", 0, 0, pbrSG.glossinessFactor); - GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE, 1); - GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_UNKNOWN, 1); + GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_FACTOR); + GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULAR_FACTOR); + mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR, pbrSG.glossinessFactor); + GetMatTex(mat, pbrSG.diffuseTexture, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_TEXTURE); + GetMatTex(mat, pbrSG.specularGlossinessTexture, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULARGLOSSINESS_TEXTURE); m->pbrSpecularGlossiness = Nullable(pbrSG); } diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 742088042..860974243 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -190,9 +190,7 @@ inline void SetMaterialTextureProperty(std::vector& embeddedTexIdxs, Asset& } mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot)); - - const char *texCoordName = (std::string(_AI_MATKEY_TEXTURE_BASE) + ".texCoord").c_str(); - mat->AddProperty(&prop.texCoord, 1, texCoordName, texType, texSlot); + mat->AddProperty(&prop.texCoord, 1, _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, texType, texSlot); if (prop.texture->sampler) { Ref sampler = prop.texture->sampler; @@ -200,18 +198,18 @@ inline void SetMaterialTextureProperty(std::vector& embeddedTexIdxs, Asset& aiString name(sampler->name); aiString id(sampler->id); - mat->AddProperty(&name, (std::string(_AI_MATKEY_MAPPING_BASE) + "name").c_str(), texType, texSlot); - mat->AddProperty(&id, (std::string(_AI_MATKEY_MAPPING_BASE) + "id").c_str(), texType, texSlot); + mat->AddProperty(&name, AI_MATKEY_GLTF_MAPPINGNAME(texType, texSlot)); + mat->AddProperty(&id, AI_MATKEY_GLTF_MAPPINGID(texType, texSlot)); mat->AddProperty(&sampler->wrapS, 1, AI_MATKEY_MAPPINGMODE_U(texType, texSlot)); mat->AddProperty(&sampler->wrapT, 1, AI_MATKEY_MAPPINGMODE_V(texType, texSlot)); if (sampler->magFilter != SamplerMagFilter::UNSET) { - mat->AddProperty(&sampler->magFilter, 1, (std::string(_AI_MATKEY_MAPPING_BASE) + "filtermag").c_str(), texType, texSlot); + mat->AddProperty(&sampler->magFilter, 1, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(texType, texSlot)); } if (sampler->minFilter != SamplerMinFilter::UNSET) { - mat->AddProperty(&sampler->minFilter, 1, (std::string(_AI_MATKEY_MAPPING_BASE) + "filtermin").c_str(), texType, texSlot); + mat->AddProperty(&sampler->minFilter, 1, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(texType, texSlot)); } } } @@ -235,9 +233,9 @@ void glTF2Importer::ImportMaterials(glTF2::Asset& r) SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE); - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, aiTextureType_UNKNOWN); - aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, "$mat.gltf.pbrMetallicRoughness.metallicFactor"); - aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, "$mat.gltf.pbrMetallicRoughness.roughnessFactor"); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); + aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR); + aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.normalTexture, aimat, aiTextureType_NORMALS); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.occlusionTexture, aimat, aiTextureType_LIGHTMAP); @@ -245,17 +243,19 @@ void glTF2Importer::ImportMaterials(glTF2::Asset& r) SetMaterialColorProperty(r, mat.emissiveFactor, aimat, AI_MATKEY_COLOR_EMISSIVE); aimat->AddProperty(&mat.doubleSided, 1, AI_MATKEY_TWOSIDED); - aimat->AddProperty(&mat.alphaMode, 1, "$mat.gltf.alphaMode"); - aimat->AddProperty(&mat.alphaCutoff, 1, "$mat.gltf.alphaCutoff"); + aimat->AddProperty(&mat.alphaMode, 1, AI_MATKEY_GLTF_ALPHAMODE); + aimat->AddProperty(&mat.alphaCutoff, 1, AI_MATKEY_GLTF_ALPHACUTOFF); //pbrSpecularGlossiness if (mat.pbrSpecularGlossiness.isPresent) { - aimat->AddProperty(&mat.pbrSpecularGlossiness.isPresent, 1, "$mat.gltf.pbrSpecularGlossiness.on"); - SetMaterialColorProperty(r, mat.pbrSpecularGlossiness.value.diffuseFactor, aimat, "$clr.diffuse", 0, 1); - SetMaterialColorProperty(r, mat.pbrSpecularGlossiness.value.specularFactor, aimat, "$clr.specular", 0, 1); - aimat->AddProperty(&mat.pbrSpecularGlossiness.value.glossinessFactor, 1, "$mat.gltf.pbrSpecularGlossiness.glossinessFactor"); - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrSpecularGlossiness.value.diffuseTexture, aimat, aiTextureType_DIFFUSE, 1); - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrSpecularGlossiness.value.specularGlossinessTexture, aimat, aiTextureType_UNKNOWN, 1); + PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value; + + aimat->AddProperty(&mat.pbrSpecularGlossiness.isPresent, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS); + SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_FACTOR); + SetMaterialColorProperty(r, pbrSG.specularFactor, aimat, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULAR_FACTOR); + aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.diffuseTexture, aimat, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.specularGlossinessTexture, aimat, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULARGLOSSINESS_TEXTURE); } } } From da6a252efbb433e25d61e0dbdae9873d3829da76 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Tue, 5 Sep 2017 18:06:59 -0400 Subject: [PATCH 048/490] Fix METALLIC_FACTOR typo --- code/glTF2Asset.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index b4369fe71..d894e9473 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -166,7 +166,7 @@ namespace glTF2 #define AI_GLB_MAGIC_NUMBER "glTF" #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0,0 - #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 + #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0 #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 0 #define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0 #define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0 From 37582131f494babbf3ad4ba878796fb0384dc085 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Wed, 6 Sep 2017 14:16:52 -0400 Subject: [PATCH 049/490] =?UTF-8?q?Set=20the=20metallicFactor=20to=200=20i?= =?UTF-8?q?f=20source=20file=20doesn=E2=80=99t=20have=20metallicFactor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise, the models have full metallic materials and appear very dim --- code/glTF2Exporter.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 931e491e3..a5c74fd46 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -418,7 +418,12 @@ void glTF2Exporter::ExportMaterials() GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_DIFFUSE); GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); - mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor); + + if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { + //if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0 + m->pbrMetallicRoughness.metallicFactor = 0; + } + mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor); GetMatTex(mat, m->normalTexture, aiTextureType_NORMALS); From 0a8183531e0fc43780e51008c04bb2d34ecb5eb5 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Wed, 6 Sep 2017 14:17:24 -0400 Subject: [PATCH 050/490] =?UTF-8?q?Set=20alphaMode,=20baseColorFactor=20op?= =?UTF-8?q?acity=20when=20model=E2=80=99s=20opacity=20isn=E2=80=99t=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- code/glTF2Exporter.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index a5c74fd46..9130c750c 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -433,7 +433,17 @@ void glTF2Exporter::ExportMaterials() mat->Get(AI_MATKEY_TWOSIDED, m->doubleSided); mat->Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff); - mat->Get(AI_MATKEY_GLTF_ALPHAMODE, m->alphaMode); + + if (mat->Get(AI_MATKEY_GLTF_ALPHAMODE, m->alphaMode) != AI_SUCCESS) { + float opacity; + + if (mat->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { + if (opacity < 1) { + m->alphaMode = "MASK"; + m->pbrMetallicRoughness.baseColorFactor[3] *= opacity; + } + } + } bool hasPbrSpecularGlossiness; mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS, hasPbrSpecularGlossiness); From a5e8e0b2bd8fbdd625920ccf448d9475f327fed5 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Wed, 6 Sep 2017 14:17:37 -0400 Subject: [PATCH 051/490] Remove commented out code --- code/glTF2Asset.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index d894e9473..12cad25ba 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -717,13 +717,6 @@ namespace glTF2 float strength = 1; }; - //! Holds a material property that can be a texture or a color (fallback for glTF 1) - /*struct FallbackTexProperty - { - Ref texture; - vec4 color; - };*/ - struct PbrMetallicRoughness { vec4 baseColorFactor; From 2ee7991558be791115087670026b9039cfac6bff Mon Sep 17 00:00:00 2001 From: John Senneker Date: Wed, 6 Sep 2017 14:49:25 -0400 Subject: [PATCH 052/490] Restrict search for OFF header to first 3 bytes --- code/OFFLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/OFFLoader.cpp b/code/OFFLoader.cpp index 2723beb0e..5038176e2 100644 --- a/code/OFFLoader.cpp +++ b/code/OFFLoader.cpp @@ -94,7 +94,7 @@ bool OFFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool { if (!pIOHandler)return true; const char* tokens[] = {"off"}; - return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); + return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1,3); } return false; } From 19876e98227ebf3a76ad994cd1f190c1b382ca82 Mon Sep 17 00:00:00 2001 From: John Senneker Date: Wed, 6 Sep 2017 14:50:02 -0400 Subject: [PATCH 053/490] Add support for importing both glTF and glTF2 files --- code/ImporterRegistry.cpp | 2 ++ code/glTF2Importer.cpp | 28 ++++++++++++---------------- code/glTFAsset.inl | 4 ---- code/glTFImporter.cpp | 26 ++++++++++---------------- 4 files changed, 24 insertions(+), 36 deletions(-) diff --git a/code/ImporterRegistry.cpp b/code/ImporterRegistry.cpp index 2b5d9c1a0..b4d2c3dcf 100644 --- a/code/ImporterRegistry.cpp +++ b/code/ImporterRegistry.cpp @@ -181,6 +181,7 @@ corresponding preprocessor flag to selectively disable formats. # include "AssbinLoader.h" #endif #ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER +# include "glTFImporter.h" # include "glTF2Importer.h" #endif #ifndef ASSIMP_BUILD_NO_C4D_IMPORTER @@ -335,6 +336,7 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out) out.push_back( new AssbinImporter() ); #endif #if ( !defined ASSIMP_BUILD_NO_GLTF_IMPORTER ) + out.push_back( new glTFImporter() ); out.push_back( new glTF2Importer() ); #endif #if ( !defined ASSIMP_BUILD_NO_C4D_IMPORTER ) diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 860974243..1ab23fd18 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -58,6 +58,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "glTF2Asset.h" // This is included here so WriteLazyDict's definition is found. #include "glTF2AssetWriter.h" +#include +#include using namespace Assimp; using namespace glTF2; @@ -100,24 +102,18 @@ const aiImporterDesc* glTF2Importer::GetInfo() const bool glTF2Importer::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { - const std::string& extension = GetExtension(pFile); + const std::string &extension = GetExtension(pFile); - if (extension == "gltf" || extension == "glb") - return true; + if (extension != "gltf") // We currently can't read glTF2 binary files (.glb) + return false; - if ((checkSig || !extension.length()) && pIOHandler) { - char buffer[4]; - - std::unique_ptr pStream(pIOHandler->Open(pFile)); - if (pStream && pStream->Read(buffer, sizeof(buffer), 1) == 1) { - if (memcmp(buffer, AI_GLB_MAGIC_NUMBER, sizeof(buffer)) == 0) { - return true; // Has GLB header - } - else if (memcmp(buffer, "{\r\n ", sizeof(buffer)) == 0 - || memcmp(buffer, "{\n ", sizeof(buffer)) == 0) { - // seems a JSON file, and we're the only format that can read them - return true; - } + if (checkSig && pIOHandler) { + glTF2::Asset asset(pIOHandler); + try { + asset.Load(pFile, extension == "glb"); + return asset.asset.version >= 2; + } catch (...) { + return false; } } diff --git a/code/glTFAsset.inl b/code/glTFAsset.inl index 91d36c59b..32fe77288 100644 --- a/code/glTFAsset.inl +++ b/code/glTFAsset.inl @@ -1243,10 +1243,6 @@ inline void AssetMetadata::Read(Document& doc) } version = std::max(statedVersion, version); - if (version == 0) { - // if missing version, we'll assume version 1... - version = 1; - } if (version != 1) { char msg[128]; diff --git a/code/glTFImporter.cpp b/code/glTFImporter.cpp index 0ded26294..6dbeb7e91 100644 --- a/code/glTFImporter.cpp +++ b/code/glTFImporter.cpp @@ -100,24 +100,18 @@ const aiImporterDesc* glTFImporter::GetInfo() const bool glTFImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { - const std::string& extension = GetExtension(pFile); + const std::string &extension = GetExtension(pFile); - if (extension == "gltf" || extension == "glb") - return true; + if (extension != "gltf" && extension != "glb") + return false; - if ((checkSig || !extension.length()) && pIOHandler) { - char buffer[4]; - - std::unique_ptr pStream(pIOHandler->Open(pFile)); - if (pStream && pStream->Read(buffer, sizeof(buffer), 1) == 1) { - if (memcmp(buffer, AI_GLB_MAGIC_NUMBER, sizeof(buffer)) == 0) { - return true; // Has GLB header - } - else if (memcmp(buffer, "{\r\n ", sizeof(buffer)) == 0 - || memcmp(buffer, "{\n ", sizeof(buffer)) == 0) { - // seems a JSON file, and we're the only format that can read them - return true; - } + if (checkSig && pIOHandler) { + glTF::Asset asset(pIOHandler); + try { + asset.Load(pFile, extension == "glb"); + return asset.asset.version < 2; + } catch (...) { + return false; } } From 140b903d7a062216016597ef30f4885b28e5c767 Mon Sep 17 00:00:00 2001 From: John Senneker Date: Wed, 6 Sep 2017 15:32:44 -0400 Subject: [PATCH 054/490] Fix parsing of glTF version Handle version as int in gltf Fix format specifiers in glTF version parser --- code/glTF2Asset.h | 4 ++-- code/glTF2Asset.inl | 29 ++++++++++++----------------- code/glTF2AssetWriter.inl | 9 ++------- code/glTF2Exporter.cpp | 2 +- code/glTF2Importer.cpp | 3 ++- code/glTFAsset.h | 4 ++-- code/glTFAsset.inl | 29 ++++++++++++++++++++--------- code/glTFAssetWriter.inl | 9 ++------- code/glTFImporter.cpp | 3 ++- 9 files changed, 45 insertions(+), 47 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 12cad25ba..1711b930b 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -1005,13 +1005,13 @@ namespace glTF2 std::string version; //!< Specifies the target rendering API (default: "1.0.3") } profile; //!< Specifies the target rendering API and version, e.g., WebGL 1.0.3. (default: {}) - float version; //!< The glTF format version + std::string version; //!< The glTF format version void Read(Document& doc); AssetMetadata() : premultipliedAlpha(false) - , version(0) + , version("") { } }; diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index e2d812563..bc8261317 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -985,15 +985,21 @@ inline void Scene::Read(Value& obj, Asset& r) inline void AssetMetadata::Read(Document& doc) { - // read the version, etc. - std::string statedVersion; - if (Value* obj = FindObject(doc, "asset")) { ReadMember(*obj, "copyright", copyright); ReadMember(*obj, "generator", generator); premultipliedAlpha = MemberOrDefault(*obj, "premultipliedAlpha", false); - statedVersion = MemberOrDefault(*obj, "version", "0.0"); + + if (Value* versionString = FindString(*obj, "version")) { + version = versionString->GetString(); + } else if (Value* versionNumber = FindNumber (*obj, "version")) { + char buf[4]; + + ai_snprintf(buf, 4, "%.1f", versionNumber->GetDouble()); + + version = buf; + } if (Value* profile = FindObject(*obj, "profile")) { ReadMember(*profile, "api", this->profile.api); @@ -1001,19 +1007,8 @@ inline void AssetMetadata::Read(Document& doc) } } - float statedFloatVersion = std::strtof(statedVersion.c_str(), 0); - - version = std::max(statedFloatVersion, version); - - if (version == 0) { - // if missing version, we'll assume version 2.0... - version = 2; - } - - if (version != 2) { - char msg[128]; - ai_snprintf(msg, 128, "GLTF: Unsupported glTF version: %.1f", version); - throw DeadlyImportError(msg); + if (version.empty() || version[0] != '2') { + throw DeadlyImportError("GLTF: Unsupported glTF version: " + version); } } diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index ad68a14e1..1665bee02 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -652,13 +652,8 @@ namespace glTF2 { { Value asset; asset.SetObject(); - { - char versionChar[10]; - ai_snprintf(versionChar, sizeof(versionChar), "%.1f", mAsset.asset.version); - asset.AddMember("version", Value(versionChar, mAl).Move(), mAl); - - asset.AddMember("generator", Value(mAsset.asset.generator, mAl).Move(), mAl); - } + asset.AddMember("version", Value(mAsset.asset.version, mAl).Move(), mAl); + asset.AddMember("generator", Value(mAsset.asset.generator, mAl).Move(), mAl); mDoc.AddMember("asset", asset, mAl); } diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 9130c750c..f3e083543 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -811,7 +811,7 @@ void glTF2Exporter::ExportScene() void glTF2Exporter::ExportMetadata() { AssetMetadata& asset = mAsset->asset; - asset.version = 2; + asset.version = "2.0"; char buffer[256]; ai_snprintf(buffer, 256, "Open Asset Import Library (assimp v%d.%d.%d)", diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 1ab23fd18..34144b9e2 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -111,7 +111,8 @@ bool glTF2Importer::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool glTF2::Asset asset(pIOHandler); try { asset.Load(pFile, extension == "glb"); - return asset.asset.version >= 2; + std::string version = asset.asset.version; + return !version.empty() && version[0] == '2'; } catch (...) { return false; } diff --git a/code/glTFAsset.h b/code/glTFAsset.h index 30d0bc056..60107bee6 100644 --- a/code/glTFAsset.h +++ b/code/glTFAsset.h @@ -1058,13 +1058,13 @@ namespace glTF std::string version; //!< Specifies the target rendering API (default: "1.0.3") } profile; //!< Specifies the target rendering API and version, e.g., WebGL 1.0.3. (default: {}) - float version; //!< The glTF format version (should be 1.0) + std::string version; //!< The glTF format version (should be 1.0) void Read(Document& doc); AssetMetadata() : premultipliedAlpha(false) - , version(0) + , version("") { } }; diff --git a/code/glTFAsset.inl b/code/glTFAsset.inl index 32fe77288..bb4bb50aa 100644 --- a/code/glTFAsset.inl +++ b/code/glTFAsset.inl @@ -40,6 +40,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "StringUtils.h" +#include // Header files, Assimp #include @@ -128,6 +129,12 @@ namespace { return (it != val.MemberEnd() && it->value.IsString()) ? &it->value : 0; } + inline Value* FindNumber(Value& val, const char* id) + { + Value::MemberIterator it = val.FindMember(id); + return (it != val.MemberEnd() && it->value.IsNumber()) ? &it->value : 0; + } + inline Value* FindArray(Value& val, const char* id) { Value::MemberIterator it = val.FindMember(id); @@ -1228,13 +1235,21 @@ inline void Scene::Read(Value& obj, Asset& r) inline void AssetMetadata::Read(Document& doc) { // read the version, etc. - float statedVersion = 0; if (Value* obj = FindObject(doc, "asset")) { ReadMember(*obj, "copyright", copyright); ReadMember(*obj, "generator", generator); premultipliedAlpha = MemberOrDefault(*obj, "premultipliedAlpha", false); - statedVersion = MemberOrDefault(*obj, "version", 0); + + if (Value* versionString = FindString(*obj, "version")) { + version = versionString->GetString(); + } else if (Value* versionNumber = FindNumber (*obj, "version")) { + char buf[4]; + + ai_snprintf(buf, 4, "%.1f", versionNumber->GetDouble()); + + version = buf; + } if (Value* profile = FindObject(*obj, "profile")) { ReadMember(*profile, "api", this->profile.api); @@ -1242,12 +1257,8 @@ inline void AssetMetadata::Read(Document& doc) } } - version = std::max(statedVersion, version); - - if (version != 1) { - char msg[128]; - ai_snprintf(msg, 128, "GLTF: Unsupported glTF version: %.0f", version); - throw DeadlyImportError(msg); + if (version.empty() || version[0] != '1') { + throw DeadlyImportError("GLTF: Unsupported glTF version: " + version); } } @@ -1269,7 +1280,7 @@ inline void Asset::ReadBinaryHeader(IOStream& stream) } AI_SWAP4(header.version); - asset.version = header.version; + asset.version = std::to_string(header.version); if (header.version != 1) { throw DeadlyImportError("GLTF: Unsupported binary glTF version"); } diff --git a/code/glTFAssetWriter.inl b/code/glTFAssetWriter.inl index 166b84fd1..698f0ba8f 100644 --- a/code/glTFAssetWriter.inl +++ b/code/glTFAssetWriter.inl @@ -606,13 +606,8 @@ namespace glTF { { Value asset; asset.SetObject(); - { - char versionChar[10]; - ai_snprintf(versionChar, sizeof(versionChar), "%.0f", mAsset.asset.version); - asset.AddMember("version", Value(versionChar, mAl).Move(), mAl); - - asset.AddMember("generator", Value(mAsset.asset.generator, mAl).Move(), mAl); - } + asset.AddMember("version", Value(mAsset.asset.version, mAl).Move(), mAl); + asset.AddMember("generator", Value(mAsset.asset.generator, mAl).Move(), mAl); mDoc.AddMember("asset", asset, mAl); } diff --git a/code/glTFImporter.cpp b/code/glTFImporter.cpp index 6dbeb7e91..1b5c953be 100644 --- a/code/glTFImporter.cpp +++ b/code/glTFImporter.cpp @@ -109,7 +109,8 @@ bool glTFImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool glTF::Asset asset(pIOHandler); try { asset.Load(pFile, extension == "glb"); - return asset.asset.version < 2; + std::string version = asset.asset.version; + return !version.empty() && version[0] == '1'; } catch (...) { return false; } From a438ece655ced52835bda0dcb492cab8394de14f Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Wed, 6 Sep 2017 23:43:43 -0400 Subject: [PATCH 055/490] Remove premultipliedAlpha from gltf2 --- code/glTF2Asset.h | 7 +------ code/glTF2Asset.inl | 2 -- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 1711b930b..8b226dacb 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -998,7 +998,6 @@ namespace glTF2 { std::string copyright; //!< A copyright message suitable for display to credit the content creator. std::string generator; //!< Tool that generated this glTF model.Useful for debugging. - bool premultipliedAlpha; //!< Specifies if the shaders were generated with premultiplied alpha. (default: false) struct { std::string api; //!< Specifies the target rendering API (default: "WebGL") @@ -1009,11 +1008,7 @@ namespace glTF2 void Read(Document& doc); - AssetMetadata() - : premultipliedAlpha(false) - , version("") - { - } + AssetMetadata() : version("") {} }; // diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index bc8261317..3d4a4ee42 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -989,8 +989,6 @@ inline void AssetMetadata::Read(Document& doc) ReadMember(*obj, "copyright", copyright); ReadMember(*obj, "generator", generator); - premultipliedAlpha = MemberOrDefault(*obj, "premultipliedAlpha", false); - if (Value* versionString = FindString(*obj, "version")) { version = versionString->GetString(); } else if (Value* versionNumber = FindNumber (*obj, "version")) { From 5cb13aa4b3b4dc282ed970ffd57238b823492f47 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Thu, 7 Sep 2017 14:55:44 -0400 Subject: [PATCH 056/490] Load gltf .bin files from correct directory --- code/glTF2Asset.inl | 4 +++- code/glTFAsset.inl | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 3d4a4ee42..03c2279e8 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -337,7 +337,9 @@ inline void Buffer::Read(Value& obj, Asset& r) } else { // Local file if (byteLength > 0) { - IOStream* file = r.OpenFile(uri, "rb"); + std::string dir = !r.mCurrentAssetDir.empty() ? (r.mCurrentAssetDir + "/") : ""; + + IOStream* file = r.OpenFile(dir + uri, "rb"); if (file) { bool ok = LoadFromStream(*file, byteLength); delete file; diff --git a/code/glTFAsset.inl b/code/glTFAsset.inl index bb4bb50aa..9284ccb02 100644 --- a/code/glTFAsset.inl +++ b/code/glTFAsset.inl @@ -316,7 +316,9 @@ inline void Buffer::Read(Value& obj, Asset& r) } else { // Local file if (byteLength > 0) { - IOStream* file = r.OpenFile(uri, "rb"); + std::string dir = !r.mCurrentAssetDir.empty() ? (r.mCurrentAssetDir + "/") : ""; + + IOStream* file = r.OpenFile(dir + uri, "rb"); if (file) { bool ok = LoadFromStream(*file, byteLength); delete file; From d518289e7217f1966b8abd33bd574943781fab31 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Thu, 7 Sep 2017 15:48:11 -0400 Subject: [PATCH 057/490] more specific token search for Collada Loader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit collada can appear in many files, such as glTFs via the “generator†field (in the form of collada2gltf) --- code/ColladaLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ColladaLoader.cpp b/code/ColladaLoader.cpp index f7ef903f4..0e970b468 100644 --- a/code/ColladaLoader.cpp +++ b/code/ColladaLoader.cpp @@ -119,7 +119,7 @@ bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, boo * might be NULL and it's our duty to return true here. */ if (!pIOHandler)return true; - const char* tokens[] = {"collada"}; + const char* tokens[] = {" Date: Thu, 7 Sep 2017 15:50:35 -0400 Subject: [PATCH 058/490] Add gltf2 basic unit test --- test/CMakeLists.txt | 5 ++- test/unit/utglTF2ImportExport.cpp | 75 +++++++++++++++++++++++++++++++ test/unit/utglTFImportExport.cpp | 2 +- 3 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 test/unit/utglTF2ImportExport.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cb559deb3..3520f9782 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -65,7 +65,7 @@ SET( TEST_SRCS unit/ut3DSImportExport.cpp unit/utACImportExport.cpp unit/utAMFImportExport.cpp - unit/utASEImportExport.cpp + unit/utASEImportExport.cpp unit/utAnim.cpp unit/AssimpAPITest.cpp unit/utB3DImportExport.cpp @@ -88,6 +88,7 @@ SET( TEST_SRCS unit/utFixInfacingNormals.cpp unit/utGenNormals.cpp unit/utglTFImportExport.cpp + unit/utglTF2ImportExport.cpp unit/utHMPImportExport.cpp unit/utIFCImportExport.cpp unit/utImporter.cpp @@ -146,7 +147,7 @@ add_executable( unit ) add_definitions(-DASSIMP_TEST_MODELS_DIR="${CMAKE_CURRENT_LIST_DIR}/models") - + SET_PROPERTY( TARGET assimp PROPERTY DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX} ) IF( WIN32 ) diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp new file mode 100644 index 000000000..09c43131d --- /dev/null +++ b/test/unit/utglTF2ImportExport.cpp @@ -0,0 +1,75 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +#include "UnitTestPCH.h" +#include "AbstractImportExportBase.h" + +#include +#include + +using namespace Assimp; + +class utglTF2ImportExport : public AbstractImportExportBase { +public: + virtual bool importerTest() { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", 0); + return nullptr != scene; + } + + virtual bool exporterTest() { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", 0 ); + EXPECT_NE( nullptr, scene ); + EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "gltf2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured_out.gltf" ) ); + + return true; + } +}; + +TEST_F( utglTF2ImportExport, importglTF2FromFileTest ) { + EXPECT_TRUE( importerTest() ); +} + +TEST_F( utglTF2ImportExport, exportglTF2FromFileTest ) { + EXPECT_TRUE( exporterTest() ); +} diff --git a/test/unit/utglTFImportExport.cpp b/test/unit/utglTFImportExport.cpp index 349dddafa..18c348299 100644 --- a/test/unit/utglTFImportExport.cpp +++ b/test/unit/utglTFImportExport.cpp @@ -55,6 +55,6 @@ public: } }; -TEST_F( utglTFImportExport, importglTFromFileTest ) { +TEST_F( utglTFImportExport, importglTFFromFileTest ) { EXPECT_TRUE( importerTest() ); } From b4f5033d899f443b55d756a65c6369204dfffae1 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Thu, 7 Sep 2017 17:15:05 -0400 Subject: [PATCH 059/490] Remove compresssed file format flag --- code/glTF2Importer.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 34144b9e2..d234afab3 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -74,8 +74,7 @@ static const aiImporterDesc desc = { "", "", "", - aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportCompressedFlavour - | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental, + aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental, 0, 0, 0, From 816e6909ca87dd8f77a8375d3fa38de004371556 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Thu, 7 Sep 2017 17:32:41 -0400 Subject: [PATCH 060/490] Remove KHR_binary_glTF code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Binary glTF is now part of the glTF2 spec. However, it’s implemented incorrectly, so will be temporarily removed --- code/glTF2Asset.h | 21 +-------- code/glTF2Asset.inl | 84 ++---------------------------------- code/glTF2AssetWriter.h | 2 - code/glTF2AssetWriter.inl | 90 +-------------------------------------- code/glTF2Exporter.cpp | 10 +---- code/glTF2Importer.cpp | 11 ++--- 6 files changed, 10 insertions(+), 208 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 8b226dacb..b093ec027 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -43,7 +43,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * Declares a glTF class to handle gltf/glb files * * glTF Extensions Support: - * KHR_binary_glTF: full * KHR_materials_pbrSpecularGlossiness full */ #ifndef GLTF2ASSET_H_INC @@ -193,17 +192,6 @@ namespace glTF2 #include "./../include/assimp/Compiler/pushpack1.h" #endif - //! For the KHR_binary_glTF extension (binary .glb file) - //! 20-byte header (+ the JSON + a "body" data section) - struct GLB_Header - { - uint8_t magic[4]; //!< Magic number: "glTF" - uint32_t version; //!< Version number - uint32_t length; //!< Total length of the Binary glTF, including header, scene, and body, in bytes - uint32_t sceneLength; //!< Length, in bytes, of the glTF scene - uint32_t sceneFormat; //!< Specifies the format of the glTF scene (see the SceneFormat enum) - } PACK_STRUCT; - #ifdef ASSIMP_API #include "./../include/assimp/Compiler/poppack1.h" #endif @@ -1049,7 +1037,6 @@ namespace glTF2 //! Keeps info about the enabled extensions struct Extensions { - bool KHR_binary_glTF; bool KHR_materials_pbrSpecularGlossiness; } extensionsUsed; @@ -1097,10 +1084,7 @@ namespace glTF2 } //! Main function - void Load(const std::string& file, bool isBinary = false); - - //! Enables the "KHR_binary_glTF" extension on the asset - void SetAsBinary(); + void Load(const std::string& file); //! Search for an available name, starting from the given strings std::string FindUniqueID(const std::string& str, const char* suffix); @@ -1109,11 +1093,8 @@ namespace glTF2 { return mBodyBuffer; } private: - void ReadBinaryHeader(IOStream& stream); - void ReadExtensionsUsed(Document& doc); - IOStream* OpenFile(std::string path, const char* mode, bool absolute = false); }; diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 03c2279e8..cbdac5d80 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -290,11 +290,6 @@ inline Buffer::~Buffer() inline const char* Buffer::TranslateId(Asset& r, const char* id) { - // Compatibility with old spec - if (r.extensionsUsed.KHR_binary_glTF && strcmp(id, "KHR_binary_glTF") == 0) { - return "binary_glTF"; - } - return id; } @@ -630,28 +625,6 @@ inline Image::Image() inline void Image::Read(Value& obj, Asset& r) { - // Check for extensions first (to detect binary embedded data) - if (Value* extensions = FindObject(obj, "extensions")) { - if (r.extensionsUsed.KHR_binary_glTF) { - if (Value* ext = FindObject(*extensions, "KHR_binary_glTF")) { - - width = MemberOrDefault(*ext, "width", 0); - height = MemberOrDefault(*ext, "height", 0); - - ReadMember(*ext, "mimeType", mimeType); - - if (Value* bufferViewVal = FindUInt(*ext, "bufferView")) { - Ref bv = r.bufferViews.Retrieve(bufferViewVal->GetUint()); - if (bv) { - mDataLength = bv->byteLength; - mData = new uint8_t[mDataLength]; - memcpy(mData, bv->buffer->GetPointer() + bv->byteOffset, mDataLength); - } - } - } - } - } - if (!mDataLength) { if (Value* uri = FindString(obj, "uri")) { const char* uristr = uri->GetString(); @@ -1016,40 +989,7 @@ inline void AssetMetadata::Read(Document& doc) // Asset methods implementation // -inline void Asset::ReadBinaryHeader(IOStream& stream) -{ - GLB_Header header; - if (stream.Read(&header, sizeof(header), 1) != 1) { - throw DeadlyImportError("GLTF: Unable to read the file header"); - } - - if (strncmp((char*)header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic)) != 0) { - throw DeadlyImportError("GLTF: Invalid binary glTF file"); - } - - AI_SWAP4(header.version); - asset.version = header.version; - if (header.version != 1) { - throw DeadlyImportError("GLTF: Unsupported binary glTF version"); - } - - AI_SWAP4(header.sceneFormat); - if (header.sceneFormat != SceneFormat_JSON) { - throw DeadlyImportError("GLTF: Unsupported binary glTF scene format"); - } - - AI_SWAP4(header.length); - AI_SWAP4(header.sceneLength); - - mSceneLength = static_cast(header.sceneLength); - - mBodyOffset = sizeof(header)+mSceneLength; - mBodyOffset = (mBodyOffset + 3) & ~3; // Round up to next multiple of 4 - - mBodyLength = header.length - mBodyOffset; -} - -inline void Asset::Load(const std::string& pFile, bool isBinary) +inline void Asset::Load(const std::string& pFile) { mCurrentAssetDir.clear(); int pos = std::max(int(pFile.rfind('/')), int(pFile.rfind('\\'))); @@ -1060,16 +1000,8 @@ inline void Asset::Load(const std::string& pFile, bool isBinary) throw DeadlyImportError("GLTF: Could not open file for reading"); } - // is binary? then read the header - if (isBinary) { - SetAsBinary(); // also creates the body buffer - ReadBinaryHeader(*stream); - } - else { - mSceneLength = stream->FileSize(); - mBodyLength = 0; - } - + mSceneLength = stream->FileSize(); + mBodyLength = 0; // read the scene data @@ -1130,15 +1062,6 @@ inline void Asset::Load(const std::string& pFile, bool isBinary) } } -inline void Asset::SetAsBinary() -{ - if (!extensionsUsed.KHR_binary_glTF) { - extensionsUsed.KHR_binary_glTF = true; - mBodyBuffer = buffers.Create("binary_glTF"); - mBodyBuffer->MarkAsSpecial(); - } -} - inline void Asset::ReadExtensionsUsed(Document& doc) { Value* extsUsed = FindArray(doc, "extensionsUsed"); @@ -1155,7 +1078,6 @@ inline void Asset::ReadExtensionsUsed(Document& doc) #define CHECK_EXT(EXT) \ if (exts.find(#EXT) != exts.end()) extensionsUsed.EXT = true; - CHECK_EXT(KHR_binary_glTF); CHECK_EXT(KHR_materials_pbrSpecularGlossiness); #undef CHECK_EXT diff --git a/code/glTF2AssetWriter.h b/code/glTF2AssetWriter.h index 97efbeab5..bce2b1bd1 100644 --- a/code/glTF2AssetWriter.h +++ b/code/glTF2AssetWriter.h @@ -43,7 +43,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * Declares a class to write gltf/glb files * * glTF Extensions Support: - * KHR_binary_glTF: full * KHR_materials_pbrSpecularGlossiness: full */ #ifndef GLTF2ASSETWRITER_H_INC @@ -82,7 +81,6 @@ public: AssetWriter(Asset& asset); void WriteFile(const char* path); - void WriteGLBFile(const char* path); }; } diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index 1665bee02..e62f2377e 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -179,21 +179,7 @@ namespace glTF2 { inline void Write(Value& obj, Image& img, AssetWriter& w) { std::string uri; - if (w.mAsset.extensionsUsed.KHR_binary_glTF && img.bufferView) { - Value exts, ext; - exts.SetObject(); - ext.SetObject(); - - ext.AddMember("bufferView", img.bufferView->index, w.mAl); - - if (!img.mimeType.empty()) - ext.AddMember("mimeType", StringRef(img.mimeType), w.mAl); - - exts.AddMember("KHR_binary_glTF", ext, w.mAl); - obj.AddMember("extensions", exts, w.mAl); - return; - } - else if (img.HasData()) { + if (img.HasData()) { uri = "data:" + (img.mimeType.empty() ? "application/octet-stream" : img.mimeType); uri += ";base64,"; Util::EncodeBase64(img.GetData(), img.GetDataLength(), uri); @@ -577,77 +563,6 @@ namespace glTF2 { } } - inline void AssetWriter::WriteGLBFile(const char* path) - { - std::unique_ptr outfile(mAsset.OpenFile(path, "wb", true)); - - if (outfile == 0) { - throw DeadlyExportError("Could not open output file: " + std::string(path)); - } - - // we will write the header later, skip its size - outfile->Seek(sizeof(GLB_Header), aiOrigin_SET); - - StringBuffer docBuffer; - Writer writer(docBuffer); - mDoc.Accept(writer); - - if (outfile->Write(docBuffer.GetString(), docBuffer.GetSize(), 1) != 1) { - throw DeadlyExportError("Failed to write scene data!"); - } - - WriteBinaryData(outfile.get(), docBuffer.GetSize()); - } - - inline void AssetWriter::WriteBinaryData(IOStream* outfile, size_t sceneLength) - { - // - // write the body data - // - - size_t bodyLength = 0; - if (Ref b = mAsset.GetBodyBuffer()) { - bodyLength = b->byteLength; - - if (bodyLength > 0) { - size_t bodyOffset = sizeof(GLB_Header) + sceneLength; - bodyOffset = (bodyOffset + 3) & ~3; // Round up to next multiple of 4 - - outfile->Seek(bodyOffset, aiOrigin_SET); - - if (outfile->Write(b->GetPointer(), b->byteLength, 1) != 1) { - throw DeadlyExportError("Failed to write body data!"); - } - } - } - - // - // write the header - // - - GLB_Header header; - memcpy(header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic)); - - header.version = 2; - AI_SWAP4(header.version); - - header.length = uint32_t(sizeof(header) + sceneLength + bodyLength); - AI_SWAP4(header.length); - - header.sceneLength = uint32_t(sceneLength); - AI_SWAP4(header.sceneLength); - - header.sceneFormat = SceneFormat_JSON; - AI_SWAP4(header.sceneFormat); - - outfile->Seek(0, aiOrigin_SET); - - if (outfile->Write(&header, 1, sizeof(header)) != sizeof(header)) { - throw DeadlyExportError("Failed to write the header!"); - } - } - - inline void AssetWriter::WriteMetadata() { Value asset; @@ -662,9 +577,6 @@ namespace glTF2 { Value exts; exts.SetArray(); { - //if (false) - // exts.PushBack(StringRef("KHR_binary_glTF"), mAl); - // This is used to export pbrSpecularGlossiness materials with GLTF 2. if (this->mAsset.extensionsUsed.KHR_materials_pbrSpecularGlossiness) { exts.PushBack(StringRef("KHR_materials_pbrSpecularGlossiness"), mAl); diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index f3e083543..e8c42128e 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -101,10 +101,6 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai mAsset.reset( new Asset( pIOSystem ) ); - if (isBinary) { - mAsset->SetAsBinary(); - } - ExportMetadata(); ExportMaterials(); @@ -121,11 +117,7 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai AssetWriter writer(*mAsset); - if (isBinary) { - writer.WriteGLBFile(filename); - } else { - writer.WriteFile(filename); - } + writer.WriteFile(filename); } /* diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index d234afab3..34f12b5af 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -74,7 +74,7 @@ static const aiImporterDesc desc = { "", "", "", - aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental, + aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental, 0, 0, 0, @@ -103,13 +103,13 @@ bool glTF2Importer::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool { const std::string &extension = GetExtension(pFile); - if (extension != "gltf") // We currently can't read glTF2 binary files (.glb) + if (extension != "gltf") // We currently can't read glTF2 binary files (.glb), yet return false; if (checkSig && pIOHandler) { glTF2::Asset asset(pIOHandler); try { - asset.Load(pFile, extension == "glb"); + asset.Load(pFile); std::string version = asset.asset.version; return !version.empty() && version[0] == '2'; } catch (...) { @@ -615,15 +615,12 @@ void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IO // read the asset file glTF2::Asset asset(pIOHandler); - asset.Load(pFile, GetExtension(pFile) == "glb"); - + asset.Load(pFile); // // Copy the data out // - - ImportEmbeddedTextures(asset); ImportMaterials(asset); From 990fe143a131f96cec74c841567e9b20230220a8 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Fri, 8 Sep 2017 15:54:03 -0400 Subject: [PATCH 061/490] =?UTF-8?q?Fix=20mesh=20primitive=E2=80=99s=20attr?= =?UTF-8?q?ibutes=E2=80=99=20names?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per spec TEXCOORD -> TEXCOORD_0 COLOR -> COLOR_0 JOINTS -> JOINTS_0 WEIGHTS -> WEIGHTS_0 Remove JOINTMATRIX since it’s not supported (and doesn’t seem to be output, anyway) TANGENT should be added at a later date --- code/glTF2AssetWriter.inl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index e62f2377e..ed599c23e 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -380,11 +380,10 @@ namespace glTF2 { { WriteAttrs(w, attrs, p.attributes.position, "POSITION"); WriteAttrs(w, attrs, p.attributes.normal, "NORMAL"); - WriteAttrs(w, attrs, p.attributes.texcoord, "TEXCOORD", true); - WriteAttrs(w, attrs, p.attributes.color, "COLOR"); - WriteAttrs(w, attrs, p.attributes.joint, "JOINT"); - WriteAttrs(w, attrs, p.attributes.jointmatrix, "JOINTMATRIX"); - WriteAttrs(w, attrs, p.attributes.weight, "WEIGHT"); + WriteAttrs(w, attrs, p.attributes.texcoord, "TEXCOORD_0", true); + WriteAttrs(w, attrs, p.attributes.color, "COLOR_0"); + WriteAttrs(w, attrs, p.attributes.joint, "JOINTS_0"); + WriteAttrs(w, attrs, p.attributes.weight, "WEIGHTS_0"); } prim.AddMember("attributes", attrs, w.mAl); } From b1a5ca451639dbaf4852580c6842b226f3ff860c Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Fri, 8 Sep 2017 15:58:09 -0400 Subject: [PATCH 062/490] Use `forceNumber` argument of `WriteAttrs` to write correct attribute names, instead --- code/glTF2AssetWriter.inl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index ed599c23e..c91313ab4 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -380,10 +380,10 @@ namespace glTF2 { { WriteAttrs(w, attrs, p.attributes.position, "POSITION"); WriteAttrs(w, attrs, p.attributes.normal, "NORMAL"); - WriteAttrs(w, attrs, p.attributes.texcoord, "TEXCOORD_0", true); - WriteAttrs(w, attrs, p.attributes.color, "COLOR_0"); - WriteAttrs(w, attrs, p.attributes.joint, "JOINTS_0"); - WriteAttrs(w, attrs, p.attributes.weight, "WEIGHTS_0"); + WriteAttrs(w, attrs, p.attributes.texcoord, "TEXCOORD", true); + WriteAttrs(w, attrs, p.attributes.color, "COLOR", true); + WriteAttrs(w, attrs, p.attributes.joint, "JOINTS", true); + WriteAttrs(w, attrs, p.attributes.weight, "WEIGHTS", true); } prim.AddMember("attributes", attrs, w.mAl); } From cde29c937c4fdefa0a2c02f2ec1a8bd49e4121fb Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Fri, 8 Sep 2017 16:32:00 -0400 Subject: [PATCH 063/490] Formatting --- code/glTF2Exporter.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index e8c42128e..e2b46b1f7 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -389,8 +389,6 @@ void glTF2Exporter::GetMatColor(const aiMaterial* mat, vec3& prop, const char* p void glTF2Exporter::ExportMaterials() { - bool& KHR_materials_pbrSpecularGlossiness = mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness; - aiString aiName; for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) { const aiMaterial* mat = mScene->mMaterials[i]; @@ -442,8 +440,8 @@ void glTF2Exporter::ExportMaterials() if (hasPbrSpecularGlossiness) { - if (!KHR_materials_pbrSpecularGlossiness) { - KHR_materials_pbrSpecularGlossiness = true; + if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) { + mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; } PbrSpecularGlossiness pbrSG; From 86a8a58d126bbd58ff42edbcc71e79fb825f6922 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Sun, 10 Sep 2017 21:04:28 -0400 Subject: [PATCH 064/490] Exclude glTF2 Exporter test when ASSIMP_BUILD_NO_EXPORT --- test/unit/utglTF2ImportExport.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 09c43131d..fa69d648a 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -55,6 +55,7 @@ public: return nullptr != scene; } +#ifndef ASSIMP_BUILD_NO_EXPORT virtual bool exporterTest() { Assimp::Importer importer; Assimp::Exporter exporter; @@ -64,12 +65,16 @@ public: return true; } +#endif // ASSIMP_BUILD_NO_EXPORT + }; TEST_F( utglTF2ImportExport, importglTF2FromFileTest ) { EXPECT_TRUE( importerTest() ); } +#ifndef ASSIMP_BUILD_NO_EXPORT TEST_F( utglTF2ImportExport, exportglTF2FromFileTest ) { EXPECT_TRUE( exporterTest() ); } +#endif // ASSIMP_BUILD_NO_EXPORT From 4b01ecaf109ae1bbd3ae918e30abdc67d7448a5a Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Mon, 11 Sep 2017 16:31:40 -0400 Subject: [PATCH 065/490] Remove simple gltf2 export unit test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Segfaults on Linux for some reason. No other tests test exporting, so it’s fine --- test/unit/utglTF2ImportExport.cpp | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index fa69d648a..8e0fcbbcf 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -43,7 +43,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include -#include using namespace Assimp; @@ -54,27 +53,8 @@ public: const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", 0); return nullptr != scene; } - -#ifndef ASSIMP_BUILD_NO_EXPORT - virtual bool exporterTest() { - Assimp::Importer importer; - Assimp::Exporter exporter; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", 0 ); - EXPECT_NE( nullptr, scene ); - EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "gltf2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured_out.gltf" ) ); - - return true; - } -#endif // ASSIMP_BUILD_NO_EXPORT - }; TEST_F( utglTF2ImportExport, importglTF2FromFileTest ) { EXPECT_TRUE( importerTest() ); } - -#ifndef ASSIMP_BUILD_NO_EXPORT -TEST_F( utglTF2ImportExport, exportglTF2FromFileTest ) { - EXPECT_TRUE( exporterTest() ); -} -#endif // ASSIMP_BUILD_NO_EXPORT From 023cb27784e61ab92f915df49f18675c28a40df2 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Tue, 12 Sep 2017 09:57:58 -0400 Subject: [PATCH 066/490] Revert "Remove simple gltf2 export unit test" This reverts commit 4b01ecaf109ae1bbd3ae918e30abdc67d7448a5a. --- test/unit/utglTF2ImportExport.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 8e0fcbbcf..fa69d648a 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -53,8 +54,27 @@ public: const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", 0); return nullptr != scene; } + +#ifndef ASSIMP_BUILD_NO_EXPORT + virtual bool exporterTest() { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", 0 ); + EXPECT_NE( nullptr, scene ); + EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "gltf2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured_out.gltf" ) ); + + return true; + } +#endif // ASSIMP_BUILD_NO_EXPORT + }; TEST_F( utglTF2ImportExport, importglTF2FromFileTest ) { EXPECT_TRUE( importerTest() ); } + +#ifndef ASSIMP_BUILD_NO_EXPORT +TEST_F( utglTF2ImportExport, exportglTF2FromFileTest ) { + EXPECT_TRUE( exporterTest() ); +} +#endif // ASSIMP_BUILD_NO_EXPORT From b0da0796c8191bc42e6cfecb765a9a4e11595a42 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Tue, 12 Sep 2017 10:07:15 -0400 Subject: [PATCH 067/490] Fix Segfault caused by losing pointer to std::string Keep std::string alive --- code/glTF2Asset.h | 14 +++++++------- code/glTF2Exporter.cpp | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index b093ec027..8604cd2a1 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -164,7 +164,7 @@ namespace glTF2 //! Magic number for GLB files #define AI_GLB_MAGIC_NUMBER "glTF" - #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0,0 + #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0 #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 0 #define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0 @@ -172,15 +172,15 @@ namespace glTF2 #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_FACTOR "$clr.diffuse", 0, 1 #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULAR_FACTOR "$clr.specular", 0, 1 - #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0,0 + #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_TEXTURE aiTextureType_DIFFUSE, 1 #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULARGLOSSINESS_TEXTURE aiTextureType_UNKNOWN, 1 - #define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE (std::string(_AI_MATKEY_TEXTURE_BASE) + ".texCoord").c_str() - #define _AI_MATKEY_GLTF_MAPPINGNAME_BASE (std::string(_AI_MATKEY_MAPPING_BASE) + "name").c_str() - #define _AI_MATKEY_GLTF_MAPPINGID_BASE (std::string(_AI_MATKEY_MAPPING_BASE) + "id").c_str() - #define _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE (std::string(_AI_MATKEY_MAPPING_BASE) + "filtermag").c_str() - #define _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE (std::string(_AI_MATKEY_MAPPING_BASE) + "filtermin").c_str() + #define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" + #define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname" + #define _AI_MATKEY_GLTF_MAPPINGID_BASE "$tex.mappingid" + #define _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE "$tex.mappingfiltermag" + #define _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE "$tex.mappingfiltermin" #define AI_MATKEY_GLTF_TEXTURE_TEXCOORD _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N #define AI_MATKEY_GLTF_MAPPINGNAME(type, N) _AI_MATKEY_GLTF_MAPPINGNAME_BASE, type, N diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index e2b46b1f7..9a0e0cca1 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -276,16 +276,16 @@ void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture, a void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int slot) { - const char* key = (std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName).c_str(); + std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName; - mat->Get(key, tt, slot, prop); + mat->Get(textureKey.c_str(), tt, slot, prop); } void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int slot) { - const char* key = (std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName).c_str(); + std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName; - mat->Get(key, tt, slot, prop); + mat->Get(textureKey.c_str(), tt, slot, prop); } void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) From eca008d5ece5a8087a158b5f8d1651938e8fac7b Mon Sep 17 00:00:00 2001 From: John Senneker Date: Tue, 12 Sep 2017 11:55:22 -0400 Subject: [PATCH 068/490] Properly move string passed to JSON writer --- code/glTF2AssetWriter.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index c91313ab4..7c0b435f2 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -300,7 +300,7 @@ namespace glTF2 { } if (m.alphaMode != "OPAQUE") { - obj.AddMember("alphaMode", m.alphaMode, w.mAl); + obj.AddMember("alphaMode", Value(m.alphaMode, w.mAl).Move(), w.mAl); } if (m.doubleSided) { From 933bbb4f1c4752ee7bed77029a8604aa8f3e7bc6 Mon Sep 17 00:00:00 2001 From: John Senneker Date: Tue, 12 Sep 2017 11:55:52 -0400 Subject: [PATCH 069/490] Manually read alphaMode material property --- code/glTF2Exporter.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 9a0e0cca1..a3940e9ea 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -424,7 +424,20 @@ void glTF2Exporter::ExportMaterials() mat->Get(AI_MATKEY_TWOSIDED, m->doubleSided); mat->Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff); - if (mat->Get(AI_MATKEY_GLTF_ALPHAMODE, m->alphaMode) != AI_SUCCESS) { + bool foundAlphaMode = false; + for (size_t i = 0; i < mat->mNumProperties; ++i) { + aiMaterialProperty *prop = mat->mProperties[i]; + if (prop->mKey != aiString("$mat.gltf.alphaMode")) + continue; + + std::string alphaMode; + for (size_t c = 0; c < prop->mDataLength; ++c) + alphaMode += prop->mData[c]; + m->alphaMode = alphaMode; + foundAlphaMode = true; + } + + if (!foundAlphaMode) { float opacity; if (mat->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { @@ -435,7 +448,7 @@ void glTF2Exporter::ExportMaterials() } } - bool hasPbrSpecularGlossiness; + bool hasPbrSpecularGlossiness = false; mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS, hasPbrSpecularGlossiness); if (hasPbrSpecularGlossiness) { From 190f034e38bc130461df3f26b0f8fe1b650658aa Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 12 Sep 2017 18:57:44 +0300 Subject: [PATCH 070/490] Add AddressSanitizer option to CMake --- CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 690351e8b..260b78698 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,10 @@ OPTION ( ASSIMP_COVERALLS "Enable this to measure test coverage." OFF ) +OPTION ( ASSIMP_ASAN + "Enable AddressSanitizer." + OFF +) OPTION ( SYSTEM_IRRXML "Use system installed Irrlicht/IrrXML library." OFF @@ -212,6 +216,11 @@ if (ASSIMP_COVERALLS) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") endif() +if (ASSIMP_ASAN) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") +endif() + INCLUDE (FindPkgMacros) INCLUDE (PrecompiledHeader) From cbedc448c6b793d664f9357c5d4559a6da1a7c23 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 12 Sep 2017 18:59:38 +0200 Subject: [PATCH 071/490] closes https://github.com/assimp/assimp/issues/1426: add Defines.h to include folder for install. --- code/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 236e6d7ef..b9f07ce86 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -61,6 +61,7 @@ SET( PUBLIC_HEADERS ${HEADER_PATH}/color4.inl ${CMAKE_CURRENT_BINARY_DIR}/../include/assimp/config.h ${HEADER_PATH}/defs.h + ${HEADER_PATH}/Defines.h ${HEADER_PATH}/cfileio.h ${HEADER_PATH}/light.h ${HEADER_PATH}/material.h From e3163ec15eaf3df28bbe0956e9ba2687f7e42396 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 12 Sep 2017 19:07:17 +0200 Subject: [PATCH 072/490] FBX: fix some minor findings. --- code/FBXAnimation.cpp | 25 ++++++---------- code/FBXCompileConfig.h | 2 +- code/FBXConverter.cpp | 17 ++++------- code/FBXDocument.cpp | 60 ++++++++++++--------------------------- code/FBXDocument.h | 33 ++++----------------- code/FBXModel.cpp | 3 -- code/FBXNodeAttribute.cpp | 15 ++++------ code/FBXParser.cpp | 10 ++----- code/FBXParser.h | 19 ------------- 9 files changed, 47 insertions(+), 137 deletions(-) diff --git a/code/FBXAnimation.cpp b/code/FBXAnimation.cpp index e905b5bfc..6f9613f0d 100644 --- a/code/FBXAnimation.cpp +++ b/code/FBXAnimation.cpp @@ -87,17 +87,16 @@ AnimationCurve::AnimationCurve(uint64_t id, const Element& element, const std::s } } - // ------------------------------------------------------------------------------------------------ AnimationCurve::~AnimationCurve() { - + // empty } - // ------------------------------------------------------------------------------------------------ -AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element& element, const std::string& name, const Document& doc, - const char* const * target_prop_whitelist /*= NULL*/, size_t whitelist_size /*= 0*/) +AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element& element, const std::string& name, + const Document& doc, const char* const * target_prop_whitelist /*= NULL*/, + size_t whitelist_size /*= 0*/) : Object(id, element, name) , target() , doc(doc) @@ -154,18 +153,16 @@ AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element& element, cons props = GetPropertyTable(doc,"AnimationCurveNode.FbxAnimCurveNode",element,sc,false); } - // ------------------------------------------------------------------------------------------------ AnimationCurveNode::~AnimationCurveNode() { - + // empty } - // ------------------------------------------------------------------------------------------------ const AnimationCurveMap& AnimationCurveNode::Curves() const { - if(curves.empty()) { + if ( curves.empty() ) { // resolve attached animation curves const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationCurve"); @@ -195,7 +192,6 @@ const AnimationCurveMap& AnimationCurveNode::Curves() const return curves; } - // ------------------------------------------------------------------------------------------------ AnimationLayer::AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc) : Object(id, element, name) @@ -207,14 +203,12 @@ AnimationLayer::AnimationLayer(uint64_t id, const Element& element, const std::s props = GetPropertyTable(doc,"AnimationLayer.FbxAnimLayer",element,sc, true); } - // ------------------------------------------------------------------------------------------------ AnimationLayer::~AnimationLayer() { - + // empty } - // ------------------------------------------------------------------------------------------------ AnimationCurveNodeList AnimationLayer::Nodes(const char* const * target_prop_whitelist /*= NULL*/, size_t whitelist_size /*= 0*/) const @@ -298,14 +292,13 @@ AnimationStack::AnimationStack(uint64_t id, const Element& element, const std::s } } - // ------------------------------------------------------------------------------------------------ AnimationStack::~AnimationStack() { - + // empty } } //!FBX } //!Assimp -#endif +#endif // ASSIMP_BUILD_NO_FBX_IMPORTER diff --git a/code/FBXCompileConfig.h b/code/FBXCompileConfig.h index d4d40b056..56aa1c787 100644 --- a/code/FBXCompileConfig.h +++ b/code/FBXCompileConfig.h @@ -66,4 +66,4 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # endif #endif -#endif +#endif // INCLUDED_AI_FBX_COMPILECONFIG_H diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index ebe7ac4e6..ff355100f 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -55,9 +55,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "StringComparison.h" #include + #include #include - #include #include @@ -931,7 +931,7 @@ bool Converter::NeedsComplexTransformationChain( const Model& model ) const TransformationComp comp = static_cast< TransformationComp >( i ); if ( comp == TransformationComp_Rotation || comp == TransformationComp_Scaling || comp == TransformationComp_Translation || - comp == TransformationComp_GeometricScaling || comp == TransformationComp_GeometricRotation || comp == TransformationComp_GeometricTranslation ) { + comp == TransformationComp_GeometricScaling || comp == TransformationComp_GeometricRotation || comp == TransformationComp_GeometricTranslation ) { continue; } @@ -949,8 +949,7 @@ std::string Converter::NameTransformationChainNode( const std::string& name, Tra return name + std::string( MAGIC_NODE_TAG ) + "_" + NameTransformationComp( comp ); } -void Converter::GenerateTransformationNodeChain( const Model& model, - std::vector& output_nodes ) +void Converter::GenerateTransformationNodeChain( const Model& model, std::vector& output_nodes ) { const PropertyTable& props = model.Props(); const Model::RotOrder rot = model.RotationOrder(); @@ -3119,7 +3118,6 @@ void Converter::InterpolateKeys( aiVectorKey* valOut, const KeyTimeList& keys, c } } - void Converter::InterpolateKeys( aiQuatKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs, const aiVector3D& def_value, double& maxTime, @@ -3140,7 +3138,6 @@ void Converter::InterpolateKeys( aiQuatKey* valOut, const KeyTimeList& keys, con valOut[ i ].mTime = temp[ i ].mTime; - GetRotationMatrix( order, temp[ i ].mValue, m ); aiQuaternion quat = aiQuaternion( aiMatrix3x3( m ) ); @@ -3159,7 +3156,6 @@ void Converter::InterpolateKeys( aiQuatKey* valOut, const KeyTimeList& keys, con } } - void Converter::ConvertTransformOrder_TRStoSRT( aiQuatKey* out_quat, aiVectorKey* out_scale, aiVectorKey* out_translation, const KeyFrameListList& scaling, @@ -3218,7 +3214,6 @@ void Converter::ConvertTransformOrder_TRStoSRT( aiQuatKey* out_quat, aiVectorKey } } - aiQuaternion Converter::EulerToQuaternion( const aiVector3D& rot, Model::RotOrder order ) { aiMatrix4x4 m; @@ -3227,7 +3222,6 @@ aiQuaternion Converter::EulerToQuaternion( const aiVector3D& rot, Model::RotOrde return aiQuaternion( aiMatrix3x3( m ) ); } - void Converter::ConvertScaleKeys( aiNodeAnim* na, const std::vector& nodes, const LayerMap& /*layers*/, int64_t start, int64_t stop, double& maxTime, @@ -3248,7 +3242,6 @@ void Converter::ConvertScaleKeys( aiNodeAnim* na, const std::vectormScalingKeys, keys, inputs, aiVector3D( 1.0f, 1.0f, 1.0f ), maxTime, minTime ); } - void Converter::ConvertTranslationKeys( aiNodeAnim* na, const std::vector& nodes, const LayerMap& /*layers*/, int64_t start, int64_t stop, @@ -3267,7 +3260,6 @@ void Converter::ConvertTranslationKeys( aiNodeAnim* na, const std::vectormPositionKeys, keys, inputs, aiVector3D( 0.0f, 0.0f, 0.0f ), maxTime, minTime ); } - void Converter::ConvertRotationKeys( aiNodeAnim* na, const std::vector& nodes, const LayerMap& /*layers*/, int64_t start, int64_t stop, @@ -3289,7 +3281,8 @@ void Converter::ConvertRotationKeys( aiNodeAnim* na, const std::vectormMeshes && !out->mNumMeshes ); + ai_assert( !out->mMeshes ); + ai_assert( !out->mNumMeshes ); // note: the trailing () ensures initialization with NULL - not // many C++ users seem to know this, so pointing it out to avoid diff --git a/code/FBXDocument.cpp b/code/FBXDocument.cpp index b19ce4cd0..9592bf31f 100644 --- a/code/FBXDocument.cpp +++ b/code/FBXDocument.cpp @@ -70,13 +70,13 @@ LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc) , id(id) , flags() { - + // empty } // ------------------------------------------------------------------------------------------------ LazyObject::~LazyObject() { - + // empty } // ------------------------------------------------------------------------------------------------ @@ -232,16 +232,15 @@ Object::Object(uint64_t id, const Element& element, const std::string& name) , name(name) , id(id) { - + // empty } // ------------------------------------------------------------------------------------------------ Object::~Object() { - + // empty } - // ------------------------------------------------------------------------------------------------ FileGlobalSettings::FileGlobalSettings(const Document& doc, std::shared_ptr props) : props(props) @@ -361,7 +360,6 @@ void Document::ReadGlobalSettings() globals.reset(new FileGlobalSettings(*this, props)); } - // ------------------------------------------------------------------------------------------------ void Document::ReadObjects() { @@ -387,7 +385,6 @@ void Document::ReadObjects() } const char* err; - const uint64_t id = ParseTokenAsID(*tok[0], err); if(err) { DOMError(err,el.second); @@ -469,8 +466,6 @@ void Document::ReadPropertyTemplates() } } - - // ------------------------------------------------------------------------------------------------ void Document::ReadConnections() { @@ -482,7 +477,6 @@ void Document::ReadConnections() } uint64_t insertionOrder = 0l; - const Scope& sconns = *econns->Compound(); const ElementCollection conns = sconns.GetCollection("C"); for(ElementMap::const_iterator it = conns.first; it != conns.second; ++it) { @@ -491,7 +485,9 @@ void Document::ReadConnections() // PP = property-property connection, ignored for now // (tokens: "PP", ID1, "Property1", ID2, "Property2") - if(type == "PP") continue; + if ( type == "PP" ) { + continue; + } const uint64_t src = ParseTokenAsID(GetRequiredToken(el,1)); const uint64_t dest = ParseTokenAsID(GetRequiredToken(el,2)); @@ -518,11 +514,10 @@ void Document::ReadConnections() } } - // ------------------------------------------------------------------------------------------------ const std::vector& Document::AnimationStacks() const { - if (!animationStacksResolved.empty() || !animationStacks.size()) { + if (!animationStacksResolved.empty() || animationStacks.empty()) { return animationStacksResolved; } @@ -540,7 +535,6 @@ const std::vector& Document::AnimationStacks() const return animationStacksResolved; } - // ------------------------------------------------------------------------------------------------ LazyObject* Document::GetObject(uint64_t id) const { @@ -551,8 +545,7 @@ LazyObject* Document::GetObject(uint64_t id) const #define MAX_CLASSNAMES 6 // ------------------------------------------------------------------------------------------------ -std::vector Document::GetConnectionsSequenced(uint64_t id, - const ConnectionMap& conns) const +std::vector Document::GetConnectionsSequenced(uint64_t id, const ConnectionMap& conns) const { std::vector temp; @@ -569,7 +562,6 @@ std::vector Document::GetConnectionsSequenced(uint64_t id, return temp; // NRVO should handle this } - // ------------------------------------------------------------------------------------------------ std::vector Document::GetConnectionsSequenced(uint64_t id, bool is_src, const ConnectionMap& conns, @@ -578,17 +570,17 @@ std::vector Document::GetConnectionsSequenced(uint64_t id, bo { ai_assert(classnames); - ai_assert(count != 0 && count <= MAX_CLASSNAMES); + ai_assert( count != 0 ); + ai_assert( count <= MAX_CLASSNAMES); size_t lenghts[MAX_CLASSNAMES]; const size_t c = count; for (size_t i = 0; i < c; ++i) { - lenghts[i] = strlen(classnames[i]); + lenghts[ i ] = strlen(classnames[i]); } std::vector temp; - const std::pair range = conns.equal_range(id); @@ -620,49 +612,40 @@ std::vector Document::GetConnectionsSequenced(uint64_t id, bo return temp; // NRVO should handle this } - // ------------------------------------------------------------------------------------------------ std::vector Document::GetConnectionsBySourceSequenced(uint64_t source) const { return GetConnectionsSequenced(source, ConnectionsBySource()); } - - // ------------------------------------------------------------------------------------------------ -std::vector Document::GetConnectionsBySourceSequenced(uint64_t dest, - const char* classname) const +std::vector Document::GetConnectionsBySourceSequenced(uint64_t dest, const char* classname) const { const char* arr[] = {classname}; return GetConnectionsBySourceSequenced(dest, arr,1); } - - // ------------------------------------------------------------------------------------------------ -std::vector Document::GetConnectionsBySourceSequenced(uint64_t source, - const char* const* classnames, size_t count) const +std::vector Document::GetConnectionsBySourceSequenced(uint64_t source, + const char* const* classnames, size_t count) const { return GetConnectionsSequenced(source, true, ConnectionsBySource(),classnames, count); } - // ------------------------------------------------------------------------------------------------ std::vector Document::GetConnectionsByDestinationSequenced(uint64_t dest, - const char* classname) const + const char* classname) const { const char* arr[] = {classname}; return GetConnectionsByDestinationSequenced(dest, arr,1); } - // ------------------------------------------------------------------------------------------------ std::vector Document::GetConnectionsByDestinationSequenced(uint64_t dest) const { return GetConnectionsSequenced(dest, ConnectionsByDestination()); } - // ------------------------------------------------------------------------------------------------ std::vector Document::GetConnectionsByDestinationSequenced(uint64_t dest, const char* const* classnames, size_t count) const @@ -671,10 +654,9 @@ std::vector Document::GetConnectionsByDestinationSequenced(ui return GetConnectionsSequenced(dest, false, ConnectionsByDestination(),classnames, count); } - // ------------------------------------------------------------------------------------------------ Connection::Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string& prop, - const Document& doc) + const Document& doc) : insertionOrder(insertionOrder) , prop(prop) @@ -687,14 +669,12 @@ Connection::Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, co ai_assert(!dest || doc.Objects().find(dest) != doc.Objects().end()); } - // ------------------------------------------------------------------------------------------------ Connection::~Connection() { - + // empty } - // ------------------------------------------------------------------------------------------------ LazyObject& Connection::LazySourceObject() const { @@ -703,7 +683,6 @@ LazyObject& Connection::LazySourceObject() const return *lazy; } - // ------------------------------------------------------------------------------------------------ LazyObject& Connection::LazyDestinationObject() const { @@ -712,7 +691,6 @@ LazyObject& Connection::LazyDestinationObject() const return *lazy; } - // ------------------------------------------------------------------------------------------------ const Object* Connection::SourceObject() const { @@ -721,7 +699,6 @@ const Object* Connection::SourceObject() const return lazy->Get(); } - // ------------------------------------------------------------------------------------------------ const Object* Connection::DestinationObject() const { @@ -734,4 +711,3 @@ const Object* Connection::DestinationObject() const } // !Assimp #endif - diff --git a/code/FBXDocument.h b/code/FBXDocument.h index 5c8bc610f..af89f53d5 100644 --- a/code/FBXDocument.h +++ b/code/FBXDocument.h @@ -338,12 +338,7 @@ public: class Model : public Object { public: - Model(uint64_t id, const Element& element, const Document& doc, const std::string& name); - virtual ~Model(); - -public: - enum RotOrder - { + enum RotOrder { RotOrder_EulerXYZ = 0, RotOrder_EulerXZY, RotOrder_EulerYZX, @@ -357,8 +352,7 @@ public: }; - enum TransformInheritance - { + enum TransformInheritance { TransformInheritance_RrSs = 0, TransformInheritance_RSrs, TransformInheritance_Rrs, @@ -366,7 +360,10 @@ public: TransformInheritance_MAX // end-of-enum sentinel }; -public: + Model(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~Model(); + fbx_simple_property(QuaternionInterpolate, int, 0) fbx_simple_property(RotationOffset, aiVector3D, aiVector3D()) @@ -443,7 +440,6 @@ public: fbx_simple_property(LODBox, bool, false) fbx_simple_property(Freeze, bool, false) -public: const std::string& Shading() const { return shading; } @@ -462,13 +458,11 @@ public: return materials; } - /** Get geometry links */ const std::vector& GetGeometry() const { return geometry; } - /** Get node attachments */ const std::vector& GetAttributes() const { return attributes; @@ -477,7 +471,6 @@ public: /** convenience method to check if the node has a Null node marker */ bool IsNull() const; - private: void ResolveLinks(const Element& element, const Document& doc); @@ -805,7 +798,6 @@ private: typedef std::vector AnimationCurveNodeList; - /** Represents a FBX animation layer (i.e. a list of node animations) */ class AnimationLayer : public Object { @@ -828,10 +820,8 @@ private: const Document& doc; }; - typedef std::vector AnimationLayerList; - /** Represents a FBX animation stack (i.e. a list of animation layers) */ class AnimationStack : public Object { @@ -839,7 +829,6 @@ public: AnimationStack(uint64_t id, const Element& element, const std::string& name, const Document& doc); virtual ~AnimationStack(); -public: fbx_simple_property(LocalStart, int64_t, 0L) fbx_simple_property(LocalStop, int64_t, 0L) fbx_simple_property(ReferenceStart, int64_t, 0L) @@ -879,7 +868,6 @@ private: typedef std::vector WeightArray; typedef std::vector WeightIndexArray; - /** DOM class for skin deformer clusters (aka subdeformers) */ class Cluster : public Deformer { @@ -924,8 +912,6 @@ private: const Model* node; }; - - /** DOM class for skin deformers */ class Skin : public Deformer { @@ -1009,10 +995,8 @@ public: typedef std::map ObjectMap; typedef std::fbx_unordered_map > PropertyTemplateMap; - typedef std::multimap ConnectionMap; - /** DOM class for global document settings, a single instance per document can * be accessed via Document.Globals(). */ class FileGlobalSettings @@ -1074,9 +1058,6 @@ private: const Document& doc; }; - - - /** DOM root for a FBX file */ class Document { @@ -1154,8 +1135,6 @@ private: const ConnectionMap&, const char* const* classnames, size_t count) const; - -private: void ReadHeader(); void ReadObjects(); void ReadPropertyTemplates(); diff --git a/code/FBXModel.cpp b/code/FBXModel.cpp index 90939c142..3d694d7d8 100644 --- a/code/FBXModel.cpp +++ b/code/FBXModel.cpp @@ -77,14 +77,12 @@ Model::Model(uint64_t id, const Element& element, const Document& doc, const std ResolveLinks(element,doc); } - // ------------------------------------------------------------------------------------------------ Model::~Model() { } - // ------------------------------------------------------------------------------------------------ void Model::ResolveLinks(const Element& element, const Document& doc) { @@ -132,7 +130,6 @@ void Model::ResolveLinks(const Element& element, const Document& doc) } } - // ------------------------------------------------------------------------------------------------ bool Model::IsNull() const { diff --git a/code/FBXNodeAttribute.cpp b/code/FBXNodeAttribute.cpp index bcf079c2e..545343c09 100644 --- a/code/FBXNodeAttribute.cpp +++ b/code/FBXNodeAttribute.cpp @@ -53,7 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { namespace FBX { - using namespace Util; +using namespace Util; // ------------------------------------------------------------------------------------------------ NodeAttribute::NodeAttribute(uint64_t id, const Element& element, const Document& doc, const std::string& name) @@ -75,7 +75,7 @@ NodeAttribute::NodeAttribute(uint64_t id, const Element& element, const Document // ------------------------------------------------------------------------------------------------ NodeAttribute::~NodeAttribute() { - + // empty } @@ -101,33 +101,30 @@ CameraSwitcher::CameraSwitcher(uint64_t id, const Element& element, const Docume } } - // ------------------------------------------------------------------------------------------------ CameraSwitcher::~CameraSwitcher() { - + // empty } - // ------------------------------------------------------------------------------------------------ Camera::Camera(uint64_t id, const Element& element, const Document& doc, const std::string& name) : NodeAttribute(id,element,doc,name) { - + // empty } - // ------------------------------------------------------------------------------------------------ Camera::~Camera() { + // empty } - // ------------------------------------------------------------------------------------------------ Light::Light(uint64_t id, const Element& element, const Document& doc, const std::string& name) : NodeAttribute(id,element,doc,name) { - + // empty } diff --git a/code/FBXParser.cpp b/code/FBXParser.cpp index 428c29a62..cf6930e22 100644 --- a/code/FBXParser.cpp +++ b/code/FBXParser.cpp @@ -224,41 +224,36 @@ Parser::Parser (const TokenList& tokens, bool is_binary) root.reset(new Scope(*this,true)); } - // ------------------------------------------------------------------------------------------------ Parser::~Parser() { + // empty } - // ------------------------------------------------------------------------------------------------ TokenPtr Parser::AdvanceToNextToken() { last = current; if (cursor == tokens.end()) { current = NULL; - } - else { + } else { current = *cursor++; } return current; } - // ------------------------------------------------------------------------------------------------ TokenPtr Parser::CurrentToken() const { return current; } - // ------------------------------------------------------------------------------------------------ TokenPtr Parser::LastToken() const { return last; } - // ------------------------------------------------------------------------------------------------ uint64_t ParseTokenAsID(const Token& t, const char*& err_out) { @@ -296,7 +291,6 @@ uint64_t ParseTokenAsID(const Token& t, const char*& err_out) return id; } - // ------------------------------------------------------------------------------------------------ size_t ParseTokenAsDim(const Token& t, const char*& err_out) { diff --git a/code/FBXParser.h b/code/FBXParser.h index b0683ccd1..4d3766d70 100644 --- a/code/FBXParser.h +++ b/code/FBXParser.h @@ -85,12 +85,9 @@ typedef std::pair Element class Element { public: - Element(const Token& key_token, Parser& parser); ~Element(); -public: - const Scope* Compound() const { return compound.get(); } @@ -104,14 +101,11 @@ public: } private: - const Token& key_token; TokenList tokens; std::unique_ptr compound; }; - - /** FBX data entity that consists of a 'scope', a collection * of not necessarily unique #Element instances. * @@ -125,14 +119,10 @@ private: * @endverbatim */ class Scope { - public: - Scope(Parser& parser, bool topLevel = false); ~Scope(); -public: - const Element* operator[] (const std::string& index) const { ElementMap::const_iterator it = elements.find(index); return it == elements.end() ? NULL : (*it).second; @@ -158,28 +148,23 @@ public: } private: - ElementMap elements; }; - /** FBX parsing class, takes a list of input tokens and generates a hierarchy * of nested #Scope instances, representing the fbx DOM.*/ class Parser { public: - /** Parse given a token list. Does not take ownership of the tokens - * the objects must persist during the entire parser lifetime */ Parser (const TokenList& tokens,bool is_binary); ~Parser(); -public: const Scope& GetRootScope() const { return *root.get(); } - bool IsBinary() const { return is_binary; } @@ -233,8 +218,6 @@ void ParseVectorDataArray(std::vector& out, const Element& el); void ParseVectorDataArray(std::vector& out, const Element& e); void ParseVectorDataArray(std::vector& out, const Element& el); - - // extract a required element from a scope, abort if the element cannot be found const Element& GetRequiredElement(const Scope& sc, const std::string& index, const Element* element = NULL); @@ -243,8 +226,6 @@ const Scope& GetRequiredScope(const Element& el); // get token at a particular index const Token& GetRequiredToken(const Element& el, unsigned int index); - - // read a 4x4 matrix from an array of 16 floats aiMatrix4x4 ReadMatrix(const Element& element); From 6a3b030094481c3a901fbc8f570bf889246c9e09 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 12 Sep 2017 21:16:59 +0200 Subject: [PATCH 073/490] MDP: fix encoding issues. --- code/MMDPmdParser.h | 74 -------------------------- code/MMDPmxParser.cpp | 3 -- code/MMDPmxParser.h | 120 ------------------------------------------ code/MMDVmdParser.h | 32 ----------- 4 files changed, 229 deletions(-) diff --git a/code/MMDPmdParser.h b/code/MMDPmdParser.h index 5439b9043..586f20a5f 100644 --- a/code/MMDPmdParser.h +++ b/code/MMDPmdParser.h @@ -49,17 +49,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace pmd { - /// ヘッダ class PmdHeader { public: - /// モデルå std::string name; - /// モデルå(英語) std::string name_english; - /// コメント std::string comment; - /// コメント(英語) std::string comment_english; bool Read(std::ifstream* stream) @@ -83,26 +78,19 @@ namespace pmd } }; - /// 頂点 class PmdVertex { public: - /// ä½ç½® float position[3]; - /// 法線 float normal[3]; - /// UV座標 float uv[2]; - /// 関連ボーンインデックス uint16_t bone_index[2]; - /// ボーンウェイト uint8_t bone_weight; - /// エッジä¸å¯è¦– bool edge_invisible; bool Read(std::ifstream* stream) @@ -117,27 +105,17 @@ namespace pmd } }; - /// æ質 class PmdMaterial { public: - /// 減衰色 float diffuse[4]; - /// 光沢度 float power; - /// 光沢色 float specular[3]; - /// 環境色 float ambient[3]; - /// トーンインデックス uint8_t toon_index; - /// エッジ uint8_t edge_flag; - /// インデックス数 uint32_t index_count; - /// テクスãƒãƒ£ãƒ•ã‚¡ã‚¤ãƒ«å std::string texture_filename; - /// スフィアファイルå std::string sphere_filename; bool Read(std::ifstream* stream) @@ -180,23 +158,15 @@ namespace pmd RotationMovement }; - /// ボーン class PmdBone { public: - /// ボーンå std::string name; - /// ボーンå(英語) std::string name_english; - /// è¦ªãƒœãƒ¼ãƒ³ç•ªå· uint16_t parent_bone_index; - /// æœ«ç«¯ãƒœãƒ¼ãƒ³ç•ªå· uint16_t tail_pos_bone_index; - /// ボーン種類 BoneType bone_type; - /// IKãƒœãƒ¼ãƒ³ç•ªå· uint16_t ik_parent_bone_index; - /// ボーンã®ãƒ˜ãƒƒãƒ‰ã®ä½ç½® float bone_head_pos[3]; void Read(std::istream *stream) @@ -219,19 +189,13 @@ namespace pmd } }; - /// IK class PmdIk { public: - /// IKãƒœãƒ¼ãƒ³ç•ªå· uint16_t ik_bone_index; - /// IKã‚¿ãƒ¼ã‚²ãƒƒãƒˆãƒœãƒ¼ãƒ³ç•ªå· uint16_t target_bone_index; - /// å†å¸°å›žæ•° uint16_t interations; - /// è§’åº¦åˆ¶é™ float angle_limit; - /// å½±éŸ¿ä¸‹ãƒœãƒ¼ãƒ³ç•ªå· std::vector ik_child_bone_index; void Read(std::istream *stream) @@ -303,7 +267,6 @@ namespace pmd } }; - /// ボーン枠用ã®æž å class PmdBoneDispName { public: @@ -338,59 +301,36 @@ namespace pmd } }; - /// è¡çªå½¢çŠ¶ enum class RigidBodyShape : uint8_t { - /// çƒ Sphere = 0, - /// 直方体 Box = 1, - /// カプセル Cpusel = 2 }; - /// 剛体タイプ enum class RigidBodyType : uint8_t { - /// ボーン追従 BoneConnected = 0, - /// 物ç†æ¼”ç®— Physics = 1, - /// 物ç†æ¼”ç®—(Boneä½ç½®åˆã›) ConnectedPhysics = 2 }; - /// 剛体 class PmdRigidBody { public: - /// åå‰ std::string name; - /// é–¢é€£ãƒœãƒ¼ãƒ³ç•ªå· uint16_t related_bone_index; - /// ã‚°ãƒ«ãƒ¼ãƒ—ç•ªå· uint8_t group_index; - /// マスク uint16_t mask; - /// 形状 RigidBodyShape shape; - /// 大ãã• float size[3]; - /// ä½ç½® float position[3]; - /// 回転 float orientation[3]; - /// è³ªé‡ float weight; - /// 移動ダンピング float linear_damping; - /// 回転ダンピング float anglar_damping; - /// å発係数 float restitution; - /// 摩擦係数 float friction; - /// 演算方法 RigidBodyType rigid_type; void Read(std::istream *stream) @@ -414,31 +354,19 @@ namespace pmd } }; - /// 剛体ã®æ‹˜æŸ class PmdConstraint { public: - /// åå‰ std::string name; - /// 剛体Aã®ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ uint32_t rigid_body_index_a; - /// 剛体Bã®ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ uint32_t rigid_body_index_b; - /// ä½ç½® float position[3]; - /// 回転 float orientation[3]; - /// 最å°ç§»å‹•åˆ¶é™ float linear_lower_limit[3]; - /// æœ€å¤§ç§»å‹•åˆ¶é™ float linear_upper_limit[3]; - /// 最å°å›žè»¢åˆ¶é™ float angular_lower_limit[3]; - /// æœ€å¤§å›žè»¢åˆ¶é™ float angular_upper_limit[3]; - /// 移動ã«å¯¾ã™ã‚‹å¾©å…ƒåŠ› float linear_stiffness[3]; - /// 回転ã«å¯¾ã™ã‚‹å¾©å…ƒåŠ› float angular_stiffness[3]; void Read(std::istream *stream) @@ -459,7 +387,6 @@ namespace pmd } }; - /// PMDモデル class PmdModel { public: @@ -491,7 +418,6 @@ namespace pmd return result; } - /// ファイルã‹ã‚‰PmdModelを生æˆã™ã‚‹ static std::unique_ptr LoadFromStream(std::ifstream *stream) { auto result = mmd::make_unique(); diff --git a/code/MMDPmxParser.cpp b/code/MMDPmxParser.cpp index 6a76c2c7f..c38bdf49a 100644 --- a/code/MMDPmxParser.cpp +++ b/code/MMDPmxParser.cpp @@ -45,7 +45,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace pmx { - /// インデックス値を読ã¿è¾¼ã‚€ int ReadIndex(std::istream *stream, int size) { switch (size) @@ -79,7 +78,6 @@ namespace pmx } } - /// 文字列を読ã¿è¾¼ã‚€ std::string ReadString(std::istream *stream, uint8_t encoding) { int size; @@ -607,7 +605,6 @@ namespace pmx this->joints[i].Read(stream, &setting); } - //// ソフトボディ //if (this->version == 2.1f) //{ // stream->read((char*) &this->soft_body_count, sizeof(int)); diff --git a/code/MMDPmxParser.h b/code/MMDPmxParser.h index 4aff7f8eb..989cffed6 100644 --- a/code/MMDPmxParser.h +++ b/code/MMDPmxParser.h @@ -49,7 +49,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace pmx { - /// インデックス設定 class PmxSetting { public: @@ -64,26 +63,17 @@ namespace pmx , rigidbody_index_size(0) {} - /// ã‚¨ãƒ³ã‚³ãƒ¼ãƒ‰æ–¹å¼ uint8_t encoding; - /// 追加UVæ•° uint8_t uv; - /// 頂点インデックスサイズ uint8_t vertex_index_size; - /// テクスãƒãƒ£ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ã‚µã‚¤ã‚º uint8_t texture_index_size; - /// マテリアルインデックスサイズ uint8_t material_index_size; - /// ボーンインデックスサイズ uint8_t bone_index_size; - /// モーフインデックスサイズ uint8_t morph_index_size; - /// 剛体インデックスサイズ uint8_t rigidbody_index_size; void Read(std::istream *stream); }; - /// 頂点スキニングタイプ enum class PmxVertexSkinningType : uint8_t { BDEF1 = 0, @@ -93,7 +83,6 @@ namespace pmx QDEF = 4, }; - /// 頂点スキニング class PmxVertexSkinning { public: @@ -200,7 +189,6 @@ namespace pmx void Read(std::istream *stresam, PmxSetting *setting); }; - /// 頂点 class PmxVertex { public: @@ -219,24 +207,16 @@ namespace pmx } } - /// ä½ç½® float position[3]; - /// 法線 float normal[3]; - /// テクスãƒãƒ£åº§æ¨™ float uv[2]; - /// 追加テクスãƒãƒ£åº§æ¨™ float uva[4][4]; - /// スキニングタイプ PmxVertexSkinningType skinning_type; - /// スキニング std::unique_ptr skinning; - /// エッジå€çŽ‡ float edge; void Read(std::istream *stream, PmxSetting *setting); }; - /// マテリアル class PmxMaterial { public: @@ -261,42 +241,25 @@ namespace pmx } } - /// モデルå std::string material_name; - /// モデル英å std::string material_english_name; - /// 減衰色 float diffuse[4]; - /// 光沢色 float specular[3]; - /// 光沢度 float specularlity; - /// 環境色 float ambient[3]; - /// æ画フラグ uint8_t flag; - /// エッジ色 float edge_color[4]; - /// エッジサイズ float edge_size; - /// アルベドテクスãƒãƒ£ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ int diffuse_texture_index; - /// スフィアテクスãƒãƒ£ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ int sphere_texture_index; - /// スフィアテクスãƒãƒ£æ¼”算モード uint8_t sphere_op_mode; - /// 共有トゥーンフラグ uint8_t common_toon_flag; - /// トゥーンテクスãƒãƒ£ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ int toon_texture_index; - /// メモ std::string memo; - /// 頂点インデックス数 int index_count; void Read(std::istream *stream, PmxSetting *setting); }; - /// リンク class PmxIkLink { public: @@ -310,18 +273,13 @@ namespace pmx } } - /// リンクボーンインデックス int link_target; - /// è§’åº¦åˆ¶é™ uint8_t angle_lock; - /// 最大制é™è§’度 float max_radian[3]; - /// 最å°åˆ¶é™è§’度 float min_radian[3]; void Read(std::istream *stream, PmxSetting *settingn); }; - /// ボーン class PmxBone { public: @@ -347,43 +305,24 @@ namespace pmx } } - /// ボーンå std::string bone_name; - /// ボーン英å std::string bone_english_name; - /// ä½ç½® float position[3]; - /// 親ボーンインデックス int parent_index; - /// 階層 int level; - /// ボーンフラグ uint16_t bone_flag; - /// 座標オフセット(has Target) float offset[3]; - /// 接続先ボーンインデックス(not has Target) int target_index; - /// 付与親ボーンインデックス int grant_parent_index; - /// 付与率 float grant_weight; - /// 固定軸ã®æ–¹å‘ float lock_axis_orientation[3]; - /// ローカル軸ã®Xè»¸æ–¹å‘ float local_axis_x_orientation[3]; - /// ローカル軸ã®Yè»¸æ–¹å‘ float local_axis_y_orientation[3]; - /// 外部親変形ã®key値 int key; - /// IKターゲットボーン int ik_target_bone_index; - /// IKループ回数 int ik_loop; - /// IKループ計算時ã®è§’度制é™(ラジアン) float ik_loop_angle_limit; - /// IKリンク数 int ik_link_count; - /// IKリンク std::unique_ptr ik_links; void Read(std::istream *stream, PmxSetting *setting); }; @@ -543,7 +482,6 @@ namespace pmx void Read(std::istream *stream, PmxSetting *setting); //override; }; - /// モーフ class PmxMorph { public: @@ -551,34 +489,21 @@ namespace pmx : offset_count(0) { } - /// モーフå std::string morph_name; - /// モーフ英å std::string morph_english_name; - /// カテゴリ MorphCategory category; - /// モーフタイプ MorphType morph_type; - /// オフセット数 int offset_count; - /// 頂点モーフé…列 std::unique_ptr vertex_offsets; - /// UVモーフé…列 std::unique_ptr uv_offsets; - /// ボーンモーフé…列 std::unique_ptr bone_offsets; - /// マテリアルモーフé…列 std::unique_ptr material_offsets; - /// グループモーフé…列 std::unique_ptr group_offsets; - /// フリップモーフé…列 std::unique_ptr flip_offsets; - /// インパルスモーフé…列 std::unique_ptr implus_offsets; void Read(std::istream *stream, PmxSetting *setting); }; - /// 枠内è¦ç´  class PmxFrameElement { public: @@ -587,14 +512,11 @@ namespace pmx , index(0) { } - /// è¦ç´ å¯¾è±¡ uint8_t element_target; - /// è¦ç´ å¯¾è±¡ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ int index; void Read(std::istream *stream, PmxSetting *setting); }; - /// 表示枠 class PmxFrame { public: @@ -603,15 +525,10 @@ namespace pmx , element_count(0) { } - /// æž å std::string frame_name; - /// 枠英å std::string frame_english_name; - /// 特殊枠フラグ uint8_t frame_flag; - /// 枠内è¦ç´ æ•° int element_count; - /// 枠内è¦ç´ é…列 std::unique_ptr elements; void Read(std::istream *stream, PmxSetting *setting); }; @@ -637,17 +554,11 @@ namespace pmx orientation[i] = 0.0f; } } - /// 剛体å std::string girid_body_name; - /// 剛体英å std::string girid_body_english_name; - /// 関連ボーンインデックス int target_bone; - /// グループ uint8_t group; - /// マスク uint16_t mask; - /// 形状 uint8_t shape; float size[3]; float position[3]; @@ -818,7 +729,6 @@ namespace pmx void Read(std::istream *stream, PmxSetting *setting); }; - /// PMXモデル class PmxModel { public: @@ -836,65 +746,35 @@ namespace pmx , soft_body_count(0) {} - /// ãƒãƒ¼ã‚¸ãƒ§ãƒ³ float version; - /// 設定 PmxSetting setting; - /// モデルå std::string model_name; - /// モデル英å std::string model_english_name; - /// コメント std::string model_comment; - /// 英語コメント std::string model_english_comment; - /// 頂点数 int vertex_count; - /// 頂点é…列 std::unique_ptr vertices; - /// インデックス数 int index_count; - /// インデックスé…列 std::unique_ptr indices; - /// テクスãƒãƒ£æ•° int texture_count; - /// テクスãƒãƒ£é…列 std::unique_ptr< std::string []> textures; - /// マテリアル数 int material_count; - /// マテリアル std::unique_ptr materials; - /// ボーン数 int bone_count; - /// ボーンé…列 std::unique_ptr bones; - /// モーフ数 int morph_count; - /// モーフé…列 std::unique_ptr morphs; - /// 表示枠数 int frame_count; - /// 表示枠é…列 std::unique_ptr frames; - /// 剛体数 int rigid_body_count; - /// 剛体é…列 std::unique_ptr rigid_bodies; - /// ジョイント数 int joint_count; - /// ジョイントé…列 std::unique_ptr joints; - /// ソフトボディ数 int soft_body_count; - /// ソフトボディé…列 std::unique_ptr soft_bodies; - /// モデルåˆæœŸåŒ– void Init(); - /// モデル読ã¿è¾¼ã¿ void Read(std::istream *stream); - ///// ファイルã‹ã‚‰ãƒ¢ãƒ‡ãƒ«ã®èª­ã¿è¾¼ã¿ //static std::unique_ptr ReadFromFile(const char *filename); - ///// 入力ストリームã‹ã‚‰ãƒ¢ãƒ‡ãƒ«ã®èª­ã¿è¾¼ã¿ //static std::unique_ptr ReadFromStream(std::istream *stream); }; } diff --git a/code/MMDVmdParser.h b/code/MMDVmdParser.h index c5057ef47..778ee1161 100644 --- a/code/MMDVmdParser.h +++ b/code/MMDVmdParser.h @@ -50,19 +50,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace vmd { - /// ボーンフレーム class VmdBoneFrame { public: - /// ボーンå std::string name; - /// ãƒ•ãƒ¬ãƒ¼ãƒ ç•ªå· int frame; - /// ä½ç½® float position[3]; - /// 回転 float orientation[4]; - /// 補間曲線 char interpolation[4][4][4]; void Read(std::istream* stream) @@ -86,15 +80,11 @@ namespace vmd } }; - /// 表情フレーム class VmdFaceFrame { public: - /// 表情å std::string face_name; - /// 表情ã®é‡ã¿ float weight; - /// ãƒ•ãƒ¬ãƒ¼ãƒ ç•ªå· uint32_t frame; void Read(std::istream* stream) @@ -114,23 +104,15 @@ namespace vmd } }; - /// カメラフレーム class VmdCameraFrame { public: - /// ãƒ•ãƒ¬ãƒ¼ãƒ ç•ªå· int frame; - /// è·é›¢ float distance; - /// ä½ç½® float position[3]; - /// 回転 float orientation[3]; - /// 補間曲線 char interpolation[6][4]; - /// 視野角 float angle; - /// ä¸æ˜Žãƒ‡ãƒ¼ã‚¿ char unknown[3]; void Read(std::istream *stream) @@ -156,15 +138,11 @@ namespace vmd } }; - /// ライトフレーム class VmdLightFrame { public: - /// ãƒ•ãƒ¬ãƒ¼ãƒ ç•ªå· int frame; - /// 色 float color[3]; - /// ä½ç½® float position[3]; void Read(std::istream* stream) @@ -182,7 +160,6 @@ namespace vmd } }; - /// IKã®æœ‰åŠ¹ç„¡åŠ¹ class VmdIkEnable { public: @@ -190,7 +167,6 @@ namespace vmd bool enable; }; - /// IKフレーム class VmdIkFrame { public: @@ -229,23 +205,15 @@ namespace vmd } }; - /// VMDモーション class VmdMotion { public: - /// モデルå std::string model_name; - /// ãƒãƒ¼ã‚¸ãƒ§ãƒ³ int version; - /// ボーンフレーム std::vector bone_frames; - /// 表情フレーム std::vector face_frames; - /// カメラフレーム std::vector camera_frames; - /// ライトフレーム std::vector light_frames; - /// IKフレーム std::vector ik_frames; static std::unique_ptr LoadFromFile(char const *filename) From 2938a259b8c55e5f37ecacf0e2e0caed9c2d7010 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 12 Sep 2017 19:09:22 +0300 Subject: [PATCH 074/490] Enable AddressSanitizer for Linux clang build --- .travis.sh | 2 +- .travis.yml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.sh b/.travis.sh index 7161fd28d..64aafe922 100755 --- a/.travis.sh +++ b/.travis.sh @@ -1,6 +1,6 @@ function generate() { - cmake -G "Unix Makefiles" -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -DBUILD_SHARED_LIBS=$SHARED_BUILD -DASSIMP_COVERALLS=$ENABLE_COVERALLS + cmake -G "Unix Makefiles" -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -DBUILD_SHARED_LIBS=$SHARED_BUILD -DASSIMP_COVERALLS=$ENABLE_COVERALLS -DASSIMP_ASAN=$ASAN } if [ $ANDROID ]; then diff --git a/.travis.yml b/.travis.yml index 38849dae4..bb1820cc4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,13 +28,13 @@ env: - secure: "lZ7pHQvl5dpZWzBQAaIMf0wqrvtcZ4wiZKeIZjf83TEsflW8+z0uTpIuN30ZV6Glth/Sq1OhLnTP5+N57fZU/1ebA5twHdvP4bS5CIUUg71/CXQZNl36xeaqvxsG/xRrdpKOsPdjAOsQ9KPTQulsX43XDLS7CasMiLvYOpqKcPc=" - PV=r8e PLATF=linux-x86_64 NDK_HOME=${TRAVIS_BUILD_DIR}/android-ndk-${PV} PATH=${PATH}:${NDK_HOME} matrix: - - os: linux LINUX=1 TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON + - os: linux LINUX=1 TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON ASAN=OFF compiler: gcc - - os: linux LINUX=1 TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF + - os: linux LINUX=1 TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=ON compiler: clang - - os: linux LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF + - os: linux LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF compiler: gcc - - os: linux LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF + - os: linux LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF compiler: clang - os: osx osx_image: xcode8.2 From b6f122ff2c20bbd7ada5f45bdcfb4c29b5370c42 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 12 Sep 2017 19:00:44 +0300 Subject: [PATCH 075/490] Fix delete / delete[] mismatch in glTFAsset --- code/glTFAsset.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/glTFAsset.inl b/code/glTFAsset.inl index 91d36c59b..0a0de2ce3 100644 --- a/code/glTFAsset.inl +++ b/code/glTFAsset.inl @@ -332,7 +332,7 @@ inline bool Buffer::LoadFromStream(IOStream& stream, size_t length, size_t baseO stream.Seek(baseOffset, aiOrigin_SET); } - mData.reset(new uint8_t[byteLength]); + mData.reset(new uint8_t[byteLength], std::default_delete()); if (stream.Read(mData.get(), byteLength, 1) != 1) { return false; From 2cc0a378ed156dbefec62dffdf994aa6901dc177 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Wed, 13 Sep 2017 11:23:12 -0400 Subject: [PATCH 076/490] Update glTF in list of importers and exporters --- Readme.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Readme.md b/Readme.md index a5c31881a..1eaa6b8e1 100644 --- a/Readme.md +++ b/Readme.md @@ -52,7 +52,8 @@ __Importers__: - DXF - ENFF - FBX -- GLB/GLTF +- glTF 1.0 + GLB +- glTF 2.0 - HMB - IFC-STEP - IRR / IRRMESH @@ -106,8 +107,8 @@ __Exporters__: - JSON (for WebGl, via https://github.com/acgessler/assimp2json) - ASSBIN - STEP -- glTF (partial) -- glTF2.0 +- glTF 1.0 (partial) +- glTF 2.0 (partial) ### Building ### Take a look into the `INSTALL` file. Our build system is CMake, if you used CMake before there is a good chance you know what to do. From afd6c4d57ddfa8428c42afb63a953f76621c3718 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 13 Sep 2017 21:40:44 +0200 Subject: [PATCH 077/490] Revert "Asan" --- .travis.sh | 2 +- .travis.yml | 8 ++++---- CMakeLists.txt | 9 --------- code/glTFAsset.inl | 2 +- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/.travis.sh b/.travis.sh index 64aafe922..7161fd28d 100755 --- a/.travis.sh +++ b/.travis.sh @@ -1,6 +1,6 @@ function generate() { - cmake -G "Unix Makefiles" -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -DBUILD_SHARED_LIBS=$SHARED_BUILD -DASSIMP_COVERALLS=$ENABLE_COVERALLS -DASSIMP_ASAN=$ASAN + cmake -G "Unix Makefiles" -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -DBUILD_SHARED_LIBS=$SHARED_BUILD -DASSIMP_COVERALLS=$ENABLE_COVERALLS } if [ $ANDROID ]; then diff --git a/.travis.yml b/.travis.yml index bb1820cc4..38849dae4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,13 +28,13 @@ env: - secure: "lZ7pHQvl5dpZWzBQAaIMf0wqrvtcZ4wiZKeIZjf83TEsflW8+z0uTpIuN30ZV6Glth/Sq1OhLnTP5+N57fZU/1ebA5twHdvP4bS5CIUUg71/CXQZNl36xeaqvxsG/xRrdpKOsPdjAOsQ9KPTQulsX43XDLS7CasMiLvYOpqKcPc=" - PV=r8e PLATF=linux-x86_64 NDK_HOME=${TRAVIS_BUILD_DIR}/android-ndk-${PV} PATH=${PATH}:${NDK_HOME} matrix: - - os: linux LINUX=1 TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON ASAN=OFF + - os: linux LINUX=1 TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON compiler: gcc - - os: linux LINUX=1 TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=ON + - os: linux LINUX=1 TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF compiler: clang - - os: linux LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF + - os: linux LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF compiler: gcc - - os: linux LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF + - os: linux LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF compiler: clang - os: osx osx_image: xcode8.2 diff --git a/CMakeLists.txt b/CMakeLists.txt index 260b78698..690351e8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,10 +78,6 @@ OPTION ( ASSIMP_COVERALLS "Enable this to measure test coverage." OFF ) -OPTION ( ASSIMP_ASAN - "Enable AddressSanitizer." - OFF -) OPTION ( SYSTEM_IRRXML "Use system installed Irrlicht/IrrXML library." OFF @@ -216,11 +212,6 @@ if (ASSIMP_COVERALLS) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") endif() -if (ASSIMP_ASAN) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") -endif() - INCLUDE (FindPkgMacros) INCLUDE (PrecompiledHeader) diff --git a/code/glTFAsset.inl b/code/glTFAsset.inl index 61afebfb4..9284ccb02 100644 --- a/code/glTFAsset.inl +++ b/code/glTFAsset.inl @@ -341,7 +341,7 @@ inline bool Buffer::LoadFromStream(IOStream& stream, size_t length, size_t baseO stream.Seek(baseOffset, aiOrigin_SET); } - mData.reset(new uint8_t[byteLength], std::default_delete()); + mData.reset(new uint8_t[byteLength]); if (stream.Read(mData.get(), byteLength, 1) != 1) { return false; From 58213804ff82c5cd7deccd9478d4b0d5770c7992 Mon Sep 17 00:00:00 2001 From: vkovalev123 Date: Thu, 14 Sep 2017 14:55:02 +0300 Subject: [PATCH 078/490] Update 3DSLoader.cpp Fixed reading of CHINK_RGBF. If reading performs on x32 platform then reading will execute right, but on x64 it`s wrong because it will read 8 bytes instead 4. --- code/3DSLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/3DSLoader.cpp b/code/3DSLoader.cpp index 522bec307..704884a57 100644 --- a/code/3DSLoader.cpp +++ b/code/3DSLoader.cpp @@ -1381,7 +1381,7 @@ void Discreet3DSImporter::ParseColorChunk( aiColor3D* out, bool acceptPercent ) bGamma = true; case Discreet3DS::CHUNK_RGBF: - if (sizeof(ai_real) * 3 > diff) { + if (sizeof(float) * 3 > diff) { *out = clrError; return; } From 94a6fc78f423e18404e13ef271cc3fdbe77472ac Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Fri, 15 Sep 2017 18:27:59 +1000 Subject: [PATCH 079/490] Addressed last remaining warning under MSVC caused by use of 'deprecated' fopen. --- contrib/irrXML/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contrib/irrXML/CMakeLists.txt b/contrib/irrXML/CMakeLists.txt index 980bd99a4..48941970a 100644 --- a/contrib/irrXML/CMakeLists.txt +++ b/contrib/irrXML/CMakeLists.txt @@ -8,6 +8,11 @@ set( IrrXML_SRCS irrXML.h ) +if ( MSVC ) + ADD_DEFINITIONS( -D_SCL_SECURE_NO_WARNINGS ) + ADD_DEFINITIONS( -D_CRT_SECURE_NO_WARNINGS ) +endif ( MSVC ) + add_library(IrrXML STATIC ${IrrXML_SRCS}) set(IRRXML_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL "IrrXML_Include" ) set(IRRXML_LIBRARY "IrrXML" CACHE INTERNAL "IrrXML" ) From f2e2f74d730e19ab2a705d6f96bc56816e667f16 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 14 Sep 2017 10:54:02 +0300 Subject: [PATCH 080/490] Add CMake flag to treat warnings as errors --- CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 690351e8b..b404e6916 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,10 @@ OPTION ( ASSIMP_COVERALLS "Enable this to measure test coverage." OFF ) +OPTION ( ASSIMP_WERRRO + "Treat warnings as errors." + OFF +) OPTION ( SYSTEM_IRRXML "Use system installed Irrlicht/IrrXML library." OFF @@ -212,6 +216,11 @@ if (ASSIMP_COVERALLS) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") endif() +if (ASSIMP_WERROR) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") +endif() + INCLUDE (FindPkgMacros) INCLUDE (PrecompiledHeader) From 0b140db0a40fb7563964029af927785fe81cd5b2 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 14 Sep 2017 11:18:02 +0300 Subject: [PATCH 081/490] glTFExporter: Silence uninitialized variable warning This is a false positive, idx_srcdata_begin is only used if comp_allow is true and in that case it's also initialized. --- code/glTFExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/glTFExporter.cpp b/code/glTFExporter.cpp index 8967a7aa7..7215a98c3 100644 --- a/code/glTFExporter.cpp +++ b/code/glTFExporter.cpp @@ -507,7 +507,7 @@ void glTFExporter::ExportMeshes() // Variables needed for compression. BEGIN. // Indices, not pointers - because pointer to buffer is changing while writing to it. - size_t idx_srcdata_begin;// Index of buffer before writing mesh data. Also, index of begin of coordinates array in buffer. + size_t idx_srcdata_begin = 0; // Index of buffer before writing mesh data. Also, index of begin of coordinates array in buffer. size_t idx_srcdata_normal = SIZE_MAX;// Index of begin of normals array in buffer. SIZE_MAX - mean that mesh has no normals. std::vector idx_srcdata_tc;// Array of indices. Every index point to begin of texture coordinates array in buffer. size_t idx_srcdata_ind;// Index of begin of coordinates indices array in buffer. From b9efc234d0cbc08f0d09135d4cca56298cf8adfd Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 14 Sep 2017 11:22:32 +0300 Subject: [PATCH 082/490] DefaultLogger: Whitespace cleanup to fix GCC misleading indentation warning --- code/DefaultLogger.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/DefaultLogger.cpp b/code/DefaultLogger.cpp index 5a6f19544..b69e18ea9 100644 --- a/code/DefaultLogger.cpp +++ b/code/DefaultLogger.cpp @@ -253,8 +253,8 @@ void DefaultLogger::kill() // Debug message void DefaultLogger::OnDebug( const char* message ) { - if ( m_Severity == Logger::NORMAL ) - return; + if ( m_Severity == Logger::NORMAL ) + return; static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; char msg[Size]; From f4a0ab81b1d711c1151bee390e0ed5189d2f5560 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 14 Sep 2017 11:25:19 +0300 Subject: [PATCH 083/490] AssbinExporter: Add Write specialization for aiColor3D --- code/AssbinExporter.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/code/AssbinExporter.cpp b/code/AssbinExporter.cpp index 4ea261718..bd2ed0b95 100644 --- a/code/AssbinExporter.cpp +++ b/code/AssbinExporter.cpp @@ -139,6 +139,17 @@ inline size_t Write(IOStream * stream, const aiVector3D& v) return t; } +// ----------------------------------------------------------------------------------- +// Serialize a color value +template <> +inline size_t Write(IOStream * stream, const aiColor3D& v) +{ + size_t t = Write(stream,v.r); + t += Write(stream,v.g); + t += Write(stream,v.b); + return t; +} + // ----------------------------------------------------------------------------------- // Serialize a color value template <> From 046c229e486369ee52ec45db37e18a022cc511d3 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Fri, 15 Sep 2017 12:41:40 +0300 Subject: [PATCH 084/490] AssbinExporter: Fix strict aliasing violation --- code/AssbinExporter.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/AssbinExporter.cpp b/code/AssbinExporter.cpp index bd2ed0b95..a6fbcd8f7 100644 --- a/code/AssbinExporter.cpp +++ b/code/AssbinExporter.cpp @@ -650,9 +650,9 @@ inline size_t WriteArray(IOStream * stream, const T* in, unsigned int size) Write(&chunk,l->mAttenuationQuadratic); } - Write(&chunk,(const aiVector3D&)l->mColorDiffuse); - Write(&chunk,(const aiVector3D&)l->mColorSpecular); - Write(&chunk,(const aiVector3D&)l->mColorAmbient); + Write(&chunk,l->mColorDiffuse); + Write(&chunk,l->mColorSpecular); + Write(&chunk,l->mColorAmbient); if (l->mType == aiLightSource_SPOT) { Write(&chunk,l->mAngleInnerCone); From f1998d52dce46b33e669f671a0672ba4dde752fc Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 14 Sep 2017 12:19:03 +0300 Subject: [PATCH 085/490] Importer: Whitespace cleanup to fix GCC misleading indentation warning --- code/Importer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/Importer.cpp b/code/Importer.cpp index eb0119693..379daab02 100644 --- a/code/Importer.cpp +++ b/code/Importer.cpp @@ -831,8 +831,8 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) pimpl->mProgressHandler->UpdatePostProcess( static_cast(pimpl->mPostProcessingSteps.size()), static_cast(pimpl->mPostProcessingSteps.size()) ); // update private scene flags - if( pimpl->mScene ) - ScenePriv(pimpl->mScene)->mPPStepsApplied |= pFlags; + if( pimpl->mScene ) + ScenePriv(pimpl->mScene)->mPPStepsApplied |= pFlags; // clear any data allocated by post-process steps pimpl->mPPShared->Clean(); From 01c50394ce1e870e744ec2537c271562276f474a Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Fri, 15 Sep 2017 12:31:52 +0300 Subject: [PATCH 086/490] FBXParser: Silence uninitialized variable warnings --- code/FBXParser.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/FBXParser.cpp b/code/FBXParser.cpp index cf6930e22..b5661caf8 100644 --- a/code/FBXParser.cpp +++ b/code/FBXParser.cpp @@ -281,7 +281,7 @@ uint64_t ParseTokenAsID(const Token& t, const char*& err_out) unsigned int length = static_cast(t.end() - t.begin()); ai_assert(length > 0); - const char* out; + const char* out = nullptr; const uint64_t id = strtoul10_64(t.begin(),&out,&length); if (out > t.end()) { err_out = "failed to parse ID (text)"; @@ -327,7 +327,7 @@ size_t ParseTokenAsDim(const Token& t, const char*& err_out) return 0; } - const char* out; + const char* out = nullptr; const size_t id = static_cast(strtoul10_64(t.begin() + 1,&out,&length)); if (out > t.end()) { err_out = "failed to parse ID"; @@ -440,7 +440,7 @@ int64_t ParseTokenAsInt64(const Token& t, const char*& err_out) unsigned int length = static_cast(t.end() - t.begin()); ai_assert(length > 0); - const char* out; + const char* out = nullptr; const int64_t id = strtol10_64(t.begin(), &out, &length); if (out > t.end()) { err_out = "failed to parse Int64 (text)"; From 234ffc0ad6a22c590209216f4d727252e75cb0b9 Mon Sep 17 00:00:00 2001 From: Richard Mitton Date: Fri, 15 Sep 2017 12:39:58 -0700 Subject: [PATCH 087/490] Fixed truncated material names The previous Unicode library change accidentally trimmed off the last character of SIB material names. --- code/SIBImporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/SIBImporter.cpp b/code/SIBImporter.cpp index 6cd398f48..3fa66bd77 100644 --- a/code/SIBImporter.cpp +++ b/code/SIBImporter.cpp @@ -201,7 +201,7 @@ static aiString ReadString(StreamReaderLE* stream, uint32_t numWChars) //ConvertUTF16toUTF8(&start, end, &dest, limit, lenientConversion); //*dest = '\0'; - str[str.size()-1] = '\0'; + str[str.size()] = '\0'; // Return the final string. aiString result = aiString((const char *)&str[0]); //delete[] str; From f602055da5e63675913bf3747b1b754dae31e0cd Mon Sep 17 00:00:00 2001 From: Richard Mitton Date: Fri, 15 Sep 2017 12:40:38 -0700 Subject: [PATCH 088/490] Added Silo 2.5 support Silo 2.5 bumps the version number of SIB files for no apparent reason. Doesn't appear to be any other changes to the file format. --- code/SIBImporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/SIBImporter.cpp b/code/SIBImporter.cpp index 3fa66bd77..4ae2ab7d4 100644 --- a/code/SIBImporter.cpp +++ b/code/SIBImporter.cpp @@ -827,7 +827,7 @@ static void ReadInstance(SIB* sib, StreamReaderLE* stream) static void CheckVersion(StreamReaderLE* stream) { uint32_t version = stream->GetU4(); - if ( version != 1 ) { + if ( version < 1 || version > 2 ) { throw DeadlyImportError( "SIB: Unsupported file version." ); } } From 5478d4d4c758f4f652399bb0597e5f3bc96a53c9 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 16 Sep 2017 15:22:37 +0300 Subject: [PATCH 089/490] travis: Move os declarations earlier --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 38849dae4..c02be9e06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,10 @@ branches: only: - master +os: + - linux + - osx + osx_image: xcode8.3 env: @@ -50,9 +54,6 @@ script: - export COVERALLS_SERVICE_NAME=travis-ci - export COVERALLS_REPO_TOKEN=abc12345 - . ./.travis.sh -os: - - linux - - osx after_success: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ${TRAVIS_BUILD_DIR} && lcov --directory . --capture --output-file coverage.info && lcov --remove coverage.info '/usr/*' 'contrib/*' 'test/*' --output-file coverage.info && lcov --list coverage.info && coveralls-lcov --source-encoding=ISO-8859-1 --repo-token=${COVERALLS_TOKEN} coverage.info ; fi From 167fb7e250a813e1f96bb7903d212a2fd490ae4b Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 16 Sep 2017 15:34:32 +0300 Subject: [PATCH 090/490] travis: Correctly minimize build matrix --- .travis.yml | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index c02be9e06..ddb3fedd9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,17 +31,29 @@ env: # COVERITY_SCAN_TOKEN - secure: "lZ7pHQvl5dpZWzBQAaIMf0wqrvtcZ4wiZKeIZjf83TEsflW8+z0uTpIuN30ZV6Glth/Sq1OhLnTP5+N57fZU/1ebA5twHdvP4bS5CIUUg71/CXQZNl36xeaqvxsG/xRrdpKOsPdjAOsQ9KPTQulsX43XDLS7CasMiLvYOpqKcPc=" - PV=r8e PLATF=linux-x86_64 NDK_HOME=${TRAVIS_BUILD_DIR}/android-ndk-${PV} PATH=${PATH}:${NDK_HOME} - matrix: - - os: linux LINUX=1 TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON + +matrix: + exclude: + - os: linux + env: + + include: + - os: linux compiler: gcc - - os: linux LINUX=1 TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF - compiler: clang - - os: linux LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF + env: LINUX=1 TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON + - os: linux compiler: gcc - - os: linux LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF - compiler: clang + env: LINUX=1 TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF + - os: linux + compiler: gcc + env: LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF + - os: linux + compiler: gcc + env: LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF - os: osx + compiler: clang osx_image: xcode8.2 + env: install: - if [ $ANDROID ]; then wget -c http://dl.google.com/android/ndk/android-ndk-${PV}-${PLATF}.tar.bz2 && tar xf android-ndk-${PV}-${PLATF}.tar.bz2 ; fi From 798d2d063ced4a922bd41b3eb9f7630830e022e8 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 16 Sep 2017 15:52:18 +0300 Subject: [PATCH 091/490] travis: Only build with xcode 8.3 --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index ddb3fedd9..6c29561e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,8 +24,6 @@ os: - linux - osx -osx_image: xcode8.3 - env: global: # COVERITY_SCAN_TOKEN @@ -52,7 +50,7 @@ matrix: env: LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF - os: osx compiler: clang - osx_image: xcode8.2 + osx_image: xcode8.3 env: install: From cf8453a21a5661e4a486c82dcc87fb5a64670d68 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 16 Sep 2017 15:53:02 +0300 Subject: [PATCH 092/490] travis: Enable ccache --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 6c29561e3..a75c5e94b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ sudo: required language: cpp +cache: ccache + before_install: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get update -qq && sudo apt-get install cmake && sudo apt-get install cmake python3 && sudo apt-get install -qq freeglut3-dev libxmu-dev libxi-dev ; echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- ; fi - 'if [ "$TRAVIS_OS_NAME" = "osx" ]; then From 69f8dc9b31550594c719ebc0358b73fa76e0cc17 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 16 Sep 2017 17:59:47 +0300 Subject: [PATCH 093/490] Travis: Disable OS X build since it doesn't do anything currently --- .travis.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index a75c5e94b..cf0988517 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,6 @@ branches: os: - linux - - osx env: global: @@ -50,10 +49,6 @@ matrix: - os: linux compiler: gcc env: LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF - - os: osx - compiler: clang - osx_image: xcode8.3 - env: install: - if [ $ANDROID ]; then wget -c http://dl.google.com/android/ndk/android-ndk-${PV}-${PLATF}.tar.bz2 && tar xf android-ndk-${PV}-${PLATF}.tar.bz2 ; fi From c207e745349a18da5366286d2e8a18a21e2ce41c Mon Sep 17 00:00:00 2001 From: Jeremy Cowles Date: Sun, 17 Sep 2017 10:06:57 -0700 Subject: [PATCH 094/490] Fix glTF 2.0 multi-primitive support Previously, only one primitive was supported, in fact memory was corrupted when more than one primitive was found per glTF mesh. In this change, each primitive is unrolled as a new Assimp Mesh, resulting in multiple Assimp meshes per node when multiple primitives exist per glTF mesh. This is required in the general case, since glTF primitives can have different material bindings and primitive modes. --- code/glTF2Importer.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 34f12b5af..6de482981 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -518,13 +518,22 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector& } if (node.mesh) { - ainode->mNumMeshes = 1; - ainode->mMeshes = new unsigned int[1]; - int k = 0; int idx = node.mesh.GetIndex(); - for (unsigned int j = meshOffsets[idx]; j < meshOffsets[idx + 1]; ++j, ++k) { + ai_assert(idx >= 0 && idx < meshOffsets.size()); + + unsigned int offBegin = meshOffsets[idx]; + unsigned int offEnd = meshOffsets[idx + 1]; + int k = 0; + + ai_assert(offEnd >= offBegin); + + ainode->mNumMeshes = offEnd - offBegin; + ainode->mMeshes = new unsigned int[ainode->mNumMeshes]; + + for (unsigned int j = offBegin; j < offEnd; ++j, ++k) { + ai_assert(k < ainode->mNumMeshes); ainode->mMeshes[k] = j; } } From 41724ace2d0fdba04330b95d4018b499a09ea73c Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Mon, 18 Sep 2017 14:10:58 +0300 Subject: [PATCH 095/490] Collada: Silence uninitialized variable warning This is a false positive. Value of 'method' is only used if 'targetMeshes' contains something and all paths through the first loop which add stuff to 'targetMeshes' also set 'method'. --- code/ColladaLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ColladaLoader.cpp b/code/ColladaLoader.cpp index 0e970b468..ccf79ed62 100644 --- a/code/ColladaLoader.cpp +++ b/code/ColladaLoader.cpp @@ -674,7 +674,7 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada:: // create morph target meshes if any std::vector targetMeshes; std::vector targetWeights; - Collada::MorphMethod method; + Collada::MorphMethod method = Collada::Normalized; for(std::map::const_iterator it = pParser.mControllerLibrary.begin(); it != pParser.mControllerLibrary.end(); it++) From 4652be8f18842cf3dc2cbcac1b3e8bd21115a2e4 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Mon, 18 Sep 2017 14:59:55 +0300 Subject: [PATCH 096/490] FIReader: Silence uninitialized variable warning This is a false positive. First time through the loop 'imod3' is always 0 so c1 is not used. It's also set so further iterations have a valid 'c1'. If 'value' is empty the switch doesn't look at 'c1' either since 'imod3' is still 0. --- code/FIReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/FIReader.cpp b/code/FIReader.cpp index 981d0b913..95b22a1b8 100755 --- a/code/FIReader.cpp +++ b/code/FIReader.cpp @@ -168,7 +168,7 @@ struct FIBase64ValueImpl: public FIBase64Value { if (!strValueValid) { strValueValid = true; std::ostringstream os; - uint8_t c1, c2; + uint8_t c1 = 0, c2; int imod3 = 0; std::vector::size_type valueSize = value.size(); for (std::vector::size_type i = 0; i < valueSize; ++i) { From b74fc9495aeb35346a0b941de3f984d17eebd3dc Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Mon, 18 Sep 2017 15:13:19 +0300 Subject: [PATCH 097/490] PlyLoader: Fix operator precedence issue in header check The previous version might read past end of buffer --- code/PlyLoader.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/code/PlyLoader.cpp b/code/PlyLoader.cpp index d348fe113..c6e862bf1 100644 --- a/code/PlyLoader.cpp +++ b/code/PlyLoader.cpp @@ -170,9 +170,10 @@ void PLYImporter::InternReadFile(const std::string& pFile, std::vector headerCheck; streamedBuffer.getNextLine(headerCheck); - if ((headerCheck.size() >= 3) && (headerCheck[0] != 'P' && headerCheck[0] != 'p') || - (headerCheck[1] != 'L' && headerCheck[1] != 'l') || - (headerCheck[2] != 'Y' && headerCheck[2] != 'y')) + if ((headerCheck.size() < 3) || + (headerCheck[0] != 'P' && headerCheck[0] != 'p') || + (headerCheck[1] != 'L' && headerCheck[1] != 'l') || + (headerCheck[2] != 'Y' && headerCheck[2] != 'y') ) { streamedBuffer.close(); throw DeadlyImportError("Invalid .ply file: Magic number \'ply\' is no there"); From 40c308af4423429452e0ee03fb3d50bc60a5d6c0 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Mon, 18 Sep 2017 15:18:45 +0300 Subject: [PATCH 098/490] glTF: Silence uninitialized variable warning This is a false positive. 'jointNamesIndex' is either set by the loop or the following conditional is false which also sets it. The undefined value is never seen by the following code. --- code/glTF2Exporter.cpp | 2 +- code/glTFExporter.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index a3940e9ea..bcd1e2858 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -541,7 +541,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref& meshRef, Ref nodeRef = mAsset.nodes.Get(aib->mName.C_Str()); nodeRef->jointName = nodeRef->name; - unsigned int jointNamesIndex; + unsigned int jointNamesIndex = 0; bool addJointToJointNames = true; for ( unsigned int idx_joint = 0; idx_joint < skinRef->jointNames.size(); ++idx_joint) { if (skinRef->jointNames[idx_joint]->jointName.compare(nodeRef->jointName) == 0) { diff --git a/code/glTFExporter.cpp b/code/glTFExporter.cpp index 7215a98c3..a5df09ac7 100644 --- a/code/glTFExporter.cpp +++ b/code/glTFExporter.cpp @@ -444,7 +444,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref& meshRef, Ref nodeRef = mAsset.nodes.Get(aib->mName.C_Str()); nodeRef->jointName = nodeRef->id; - unsigned int jointNamesIndex; + unsigned int jointNamesIndex = 0; bool addJointToJointNames = true; for ( unsigned int idx_joint = 0; idx_joint < skinRef->jointNames.size(); ++idx_joint) { if (skinRef->jointNames[idx_joint]->jointName.compare(nodeRef->jointName) == 0) { From 982430c3ce408201ca6201b204b35e382ec0e101 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Mon, 18 Sep 2017 15:21:51 +0300 Subject: [PATCH 099/490] BlenderDNA: Silence warning about inline function which is declared but not defined It's a templated method which is meant to be specialized. The base version is never called. Just remove 'inline' to make GCC shut up. --- code/BlenderDNA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/BlenderDNA.h b/code/BlenderDNA.h index de0a5a62f..4c7d0fc26 100644 --- a/code/BlenderDNA.h +++ b/code/BlenderDNA.h @@ -253,7 +253,7 @@ public: * a compiler complain is the result. * @param dest Destination value to be written * @param db File database, including input stream. */ - template inline void Convert (T& dest, const FileDatabase& db) const; + template void Convert (T& dest, const FileDatabase& db) const; // -------------------------------------------------------- // generic converter From 5147acfe65978fd9d5400c4d39f521c85f9a660c Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Sun, 17 Sep 2017 15:11:01 -0400 Subject: [PATCH 100/490] Revert "store node mesh vs. meshes" This reverts commit a0d97505e5a53a9aa3a734d6bd3ad790234e6e09. --- code/glTF2Asset.h | 2 +- code/glTF2Asset.inl | 9 ++++++++- code/glTF2AssetWriter.inl | 4 +--- code/glTF2Exporter.cpp | 19 ++++++++++--------- code/glTF2Importer.cpp | 28 +++++++++++++--------------- 5 files changed, 33 insertions(+), 29 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 8604cd2a1..e2b61c646 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -781,7 +781,7 @@ namespace glTF2 struct Node : public Object { std::vector< Ref > children; - Ref mesh; + std::vector< Ref > meshes; Nullable matrix; Nullable translation; diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index cbdac5d80..0a3e2b051 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -934,9 +934,16 @@ inline void Node::Read(Value& obj, Asset& r) } if (Value* mesh = FindUInt(obj, "mesh")) { + //unsigned numMeshes = (unsigned)meshes->Size(); + unsigned numMeshes = 1; + + //std::vector meshList; + + this->meshes.reserve(numMeshes); + Ref meshRef = r.meshes.Retrieve((*mesh).GetUint()); - if (meshRef) this->mesh = meshRef; + if (meshRef) this->meshes.push_back(meshRef); } if (Value* camera = FindUInt(obj, "camera")) { diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index 7c0b435f2..dd90ec576 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -417,9 +417,7 @@ namespace glTF2 { AddRefsVector(obj, "children", n.children, w.mAl); - if (n.mesh) { - obj.AddMember("mesh", n.mesh->index, w.mAl); - } + AddRefsVector(obj, "meshes", n.meshes, w.mAl); AddRefsVector(obj, "skeletons", n.skeletons, w.mAl); diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index a3940e9ea..56932dba7 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -476,15 +476,16 @@ void glTF2Exporter::ExportMaterials() */ bool FindMeshNode(Ref& nodeIn, Ref& meshNode, std::string meshID) { - - if (nodeIn->mesh && meshID.compare(nodeIn->mesh->id) == 0) { - meshNode = nodeIn; - return true; + for (unsigned int i = 0; i < nodeIn->meshes.size(); ++i) { + if (meshID.compare(nodeIn->meshes[i]->id) == 0) { + meshNode = nodeIn; + return true; + } } for (unsigned int i = 0; i < nodeIn->children.size(); ++i) { if(FindMeshNode(nodeIn->children[i], meshNode, meshID)) { - return true; + return true; } } @@ -755,8 +756,8 @@ unsigned int glTF2Exporter::ExportNodeHierarchy(const aiNode* n) CopyValue(n->mTransformation, node->matrix.value); } - if (n->mNumMeshes > 0) { - node->mesh = mAsset->meshes.Get(n->mMeshes[0]); + for (unsigned int i = 0; i < n->mNumMeshes; ++i) { + node->meshes.push_back(mAsset->meshes.Get(n->mMeshes[i])); } for (unsigned int i = 0; i < n->mNumChildren; ++i) { @@ -784,8 +785,8 @@ unsigned int glTF2Exporter::ExportNode(const aiNode* n, Ref& parent) CopyValue(n->mTransformation, node->matrix.value); } - if (n->mNumMeshes > 0) { - node->mesh = mAsset->meshes.Get(n->mMeshes[0]); + for (unsigned int i = 0; i < n->mNumMeshes; ++i) { + node->meshes.push_back(mAsset->meshes.Get(n->mMeshes[i])); } for (unsigned int i = 0; i < n->mNumChildren; ++i) { diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 6de482981..d9dade3b2 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -517,24 +517,22 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector& } } - if (node.mesh) { + if (!node.meshes.empty()) { + int count = 0; + for (size_t i = 0; i < node.meshes.size(); ++i) { + int idx = node.meshes[i].GetIndex(); + count += meshOffsets[idx + 1] - meshOffsets[idx]; + } + ainode->mNumMeshes = count; - int idx = node.mesh.GetIndex(); + ainode->mMeshes = new unsigned int[count]; - ai_assert(idx >= 0 && idx < meshOffsets.size()); - - unsigned int offBegin = meshOffsets[idx]; - unsigned int offEnd = meshOffsets[idx + 1]; int k = 0; - - ai_assert(offEnd >= offBegin); - - ainode->mNumMeshes = offEnd - offBegin; - ainode->mMeshes = new unsigned int[ainode->mNumMeshes]; - - for (unsigned int j = offBegin; j < offEnd; ++j, ++k) { - ai_assert(k < ainode->mNumMeshes); - ainode->mMeshes[k] = j; + for (size_t i = 0; i < node.meshes.size(); ++i) { + int idx = node.meshes[i].GetIndex(); + for (unsigned int j = meshOffsets[idx]; j < meshOffsets[idx + 1]; ++j, ++k) { + ainode->mMeshes[k] = j; + } } } From 28523232cf735a28ec794a557af48163d84174c0 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Sun, 17 Sep 2017 17:00:57 -0400 Subject: [PATCH 101/490] Merge multiple meshes in a node into one mesh with many primtives; write out only one mesh per node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To do: - clean up MergeMeshes - see if there’s a way to do this earlier in the flow --- code/glTF2AssetWriter.inl | 4 +++- code/glTF2Exporter.cpp | 22 ++++++++++++++++++++++ code/glTF2Exporter.h | 1 + 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index dd90ec576..df28cb681 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -417,7 +417,9 @@ namespace glTF2 { AddRefsVector(obj, "children", n.children, w.mAl); - AddRefsVector(obj, "meshes", n.meshes, w.mAl); + if (!n.meshes.empty()) { + obj.AddMember("mesh", n.meshes[0]->index, w.mAl); + } AddRefsVector(obj, "skeletons", n.skeletons, w.mAl); diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 56932dba7..f63c8e89c 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -110,6 +110,7 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai } ExportMeshes(); + MergeMeshes(); ExportScene(); @@ -743,6 +744,27 @@ void glTF2Exporter::ExportMeshes() } } +void glTF2Exporter::MergeMeshes() +{ + for (unsigned int n = 0; n < mAsset->nodes.Size(); ++n) { + Ref node = mAsset->nodes.Get(n); + + unsigned int nMeshes = node->meshes.size(); + + if (nMeshes) { + Ref firstMesh = node->meshes.at(0); + + for (unsigned int m = 1; m < nMeshes; ++m) { + Ref mesh = node->meshes.at(m); + Mesh::Primitive primitive = mesh->primitives.at(0); + firstMesh->primitives.push_back(primitive); + } + + node->meshes.erase(node->meshes.begin() + 1, node->meshes.end()); + } + } +} + /* * Export the root node of the node hierarchy. * Calls ExportNode for all children. diff --git a/code/glTF2Exporter.h b/code/glTF2Exporter.h index 420c2afa6..3aed35ae6 100644 --- a/code/glTF2Exporter.h +++ b/code/glTF2Exporter.h @@ -120,6 +120,7 @@ namespace Assimp void ExportMetadata(); void ExportMaterials(); void ExportMeshes(); + void MergeMeshes(); unsigned int ExportNodeHierarchy(const aiNode* n); unsigned int ExportNode(const aiNode* node, glTF2::Ref& parent); void ExportScene(); From 814e8b3f8eb2f10263378f0a840f3d3d55a98916 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Mon, 18 Sep 2017 10:20:02 -0400 Subject: [PATCH 102/490] Formatting --- code/glTF2Asset.inl | 3 --- 1 file changed, 3 deletions(-) diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 0a3e2b051..47904b38f 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -934,11 +934,8 @@ inline void Node::Read(Value& obj, Asset& r) } if (Value* mesh = FindUInt(obj, "mesh")) { - //unsigned numMeshes = (unsigned)meshes->Size(); unsigned numMeshes = 1; - //std::vector meshList; - this->meshes.reserve(numMeshes); Ref meshRef = r.meshes.Retrieve((*mesh).GetUint()); From 2efd2cdef88f498eb68c143902804d41e8ebcc50 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Mon, 18 Sep 2017 10:43:05 -0400 Subject: [PATCH 103/490] tweaks to primitive merging logic; comments + formatting --- code/glTF2Exporter.cpp | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index f63c8e89c..888721c41 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -744,6 +744,7 @@ void glTF2Exporter::ExportMeshes() } } +//merges a node's multiple meshes (with one primitive each) into one mesh with multiple primitives void glTF2Exporter::MergeMeshes() { for (unsigned int n = 0; n < mAsset->nodes.Size(); ++n) { @@ -751,16 +752,34 @@ void glTF2Exporter::MergeMeshes() unsigned int nMeshes = node->meshes.size(); - if (nMeshes) { + //skip if it's 1 or less meshes per node + if (nMeshes > 1) { Ref firstMesh = node->meshes.at(0); + Mesh::Primitive firstPrimitive = firstMesh->primitives.at(0); - for (unsigned int m = 1; m < nMeshes; ++m) { + //loop backwards to allow easy removal of a mesh from a node once it's merged + for (unsigned int m = nMeshes - 1; m >= 1; --m) { Ref mesh = node->meshes.at(m); - Mesh::Primitive primitive = mesh->primitives.at(0); - firstMesh->primitives.push_back(primitive); + bool primitivesPushed = false; + + for (unsigned int p = 0; p < mesh->primitives.size(); ++p) { + Mesh::Primitive primitive = mesh->primitives.at(p); + + if (firstPrimitive.mode == primitive.mode) { + firstMesh->primitives.push_back(primitive); + primitivesPushed = true; + } + } + + if (primitivesPushed) { + //remove the merged meshes from the node + node->meshes.erase(node->meshes.begin() + m); + } } - node->meshes.erase(node->meshes.begin() + 1, node->meshes.end()); + //since we were looping backwards, reverse the order of merged primitives to their original order + std::reverse(firstMesh->primitives.begin() + 1, firstMesh->primitives.end()); + } } } From 8743d28ec54df8d303097ebd0c7741f6e882c21f Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Mon, 18 Sep 2017 12:16:30 -0400 Subject: [PATCH 104/490] SImplify mesh merging code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit My assumption that primitives of different types (modes) can’t be in the same mesh was incorrect. --- code/glTF2Exporter.cpp | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 888721c41..576e8c723 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -755,31 +755,18 @@ void glTF2Exporter::MergeMeshes() //skip if it's 1 or less meshes per node if (nMeshes > 1) { Ref firstMesh = node->meshes.at(0); - Mesh::Primitive firstPrimitive = firstMesh->primitives.at(0); //loop backwards to allow easy removal of a mesh from a node once it's merged for (unsigned int m = nMeshes - 1; m >= 1; --m) { Ref mesh = node->meshes.at(m); - bool primitivesPushed = false; - for (unsigned int p = 0; p < mesh->primitives.size(); ++p) { - Mesh::Primitive primitive = mesh->primitives.at(p); + firstMesh->primitives.insert(firstMesh->primitives.end(), mesh->primitives.begin(), mesh->primitives.end()); - if (firstPrimitive.mode == primitive.mode) { - firstMesh->primitives.push_back(primitive); - primitivesPushed = true; - } - } - - if (primitivesPushed) { - //remove the merged meshes from the node - node->meshes.erase(node->meshes.begin() + m); - } + node->meshes.erase(node->meshes.begin() + m); } //since we were looping backwards, reverse the order of merged primitives to their original order std::reverse(firstMesh->primitives.begin() + 1, firstMesh->primitives.end()); - } } } From de0bf2ea966905c4fdc70257c68b60a0e2d3aa22 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Mon, 18 Sep 2017 12:19:55 -0400 Subject: [PATCH 105/490] Fix alphaMode storage and reading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit alphaMode is now converted from a std::string to an aiString and back to std::string, since aiString is easier to store and retrieve from aiMaterial properties than std::string Fixes issues of alphaMode being written out as `\fOPAQUE\0\0\0\0\0\0…` --- code/glTF2Exporter.cpp | 17 ++++------------- code/glTF2Importer.cpp | 4 +++- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index a3940e9ea..aa7d5224a 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -424,20 +424,11 @@ void glTF2Exporter::ExportMaterials() mat->Get(AI_MATKEY_TWOSIDED, m->doubleSided); mat->Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff); - bool foundAlphaMode = false; - for (size_t i = 0; i < mat->mNumProperties; ++i) { - aiMaterialProperty *prop = mat->mProperties[i]; - if (prop->mKey != aiString("$mat.gltf.alphaMode")) - continue; + aiString alphaMode; - std::string alphaMode; - for (size_t c = 0; c < prop->mDataLength; ++c) - alphaMode += prop->mData[c]; - m->alphaMode = alphaMode; - foundAlphaMode = true; - } - - if (!foundAlphaMode) { + if (mat->Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) { + m->alphaMode = alphaMode.C_Str(); + } else { float opacity; if (mat->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 6de482981..0c0b13bdb 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -239,7 +239,9 @@ void glTF2Importer::ImportMaterials(glTF2::Asset& r) SetMaterialColorProperty(r, mat.emissiveFactor, aimat, AI_MATKEY_COLOR_EMISSIVE); aimat->AddProperty(&mat.doubleSided, 1, AI_MATKEY_TWOSIDED); - aimat->AddProperty(&mat.alphaMode, 1, AI_MATKEY_GLTF_ALPHAMODE); + + aiString alphaMode(mat.alphaMode); + aimat->AddProperty(&alphaMode, AI_MATKEY_GLTF_ALPHAMODE); aimat->AddProperty(&mat.alphaCutoff, 1, AI_MATKEY_GLTF_ALPHACUTOFF); //pbrSpecularGlossiness From 798542d7bde204b8bf896d260885be76065d40aa Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Mon, 18 Sep 2017 14:48:07 -0400 Subject: [PATCH 106/490] Formatting --- code/glTF2Exporter.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 576e8c723..fb94e1c95 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -479,14 +479,14 @@ bool FindMeshNode(Ref& nodeIn, Ref& meshNode, std::string meshID) { for (unsigned int i = 0; i < nodeIn->meshes.size(); ++i) { if (meshID.compare(nodeIn->meshes[i]->id) == 0) { - meshNode = nodeIn; - return true; + meshNode = nodeIn; + return true; } } for (unsigned int i = 0; i < nodeIn->children.size(); ++i) { if(FindMeshNode(nodeIn->children[i], meshNode, meshID)) { - return true; + return true; } } From 3e8955faf58a04531d3c3d4124821b3da2ced7ef Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Mon, 18 Sep 2017 18:16:48 -0400 Subject: [PATCH 107/490] =?UTF-8?q?Don=E2=80=99t=20ignore=20rgba(1,1,1,1)?= =?UTF-8?q?=20color=20properties?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not ignore rgba(1,1,1,1) material properties when importing glTF1. While a white diffuse color may be the default value for a default assimp material, `1,1,1,1` is a very explicit color value for ambient/specular/emissive color properties. Closes #1434 --- code/glTFImporter.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/code/glTFImporter.cpp b/code/glTFImporter.cpp index 1b5c953be..a1d8e0cd0 100644 --- a/code/glTFImporter.cpp +++ b/code/glTFImporter.cpp @@ -174,9 +174,7 @@ inline void SetMaterialColorProperty(std::vector& embeddedTexIdxs, Asset& r else { aiColor4D col; CopyValue(prop.color, col); - if (col.r != 1.f || col.g != 1.f || col.b != 1.f || col.a != 1.f) { - mat->AddProperty(&col, 1, pKey, type, idx); - } + mat->AddProperty(&col, 1, pKey, type, idx); } } @@ -672,7 +670,7 @@ void glTFImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOS //pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; MakeVerboseFormatProcess process; process.Execute(pScene); - + if (pScene->mNumMeshes == 0) { pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; From af9596674db63571359f163bc72125667538ad8b Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 19 Sep 2017 00:31:41 +0200 Subject: [PATCH 108/490] FBX: add missing inversion of postrotation matrix for fbx. --- code/FBXConverter.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index f53ae0218..8d85bb8ad 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -1065,6 +1065,10 @@ void Converter::GenerateTransformationNodeChain( const Model& model, continue; } + if ( comp == TransformationComp_PostRotation ) { + chain[ i ] = chain[ i ].Inverse(); + } + aiNode* nd = new aiNode(); output_nodes.push_back( nd ); From 059a32654e660ac9dcfd9a31a8436fce6042c66f Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 24 Sep 2017 19:29:43 +1000 Subject: [PATCH 109/490] Addressed warnings generated on MSVC across x86 and x64. --- code/MMDImporter.cpp | 2 +- code/glTF2Exporter.cpp | 2 +- samples/SimpleOpenGL/CMakeLists.txt | 5 +++++ samples/SimpleTexturedOpenGL/CMakeLists.txt | 5 +++++ tools/assimp_view/stdafx.h | 1 - 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/code/MMDImporter.cpp b/code/MMDImporter.cpp index 84797cba2..c40978c0e 100644 --- a/code/MMDImporter.cpp +++ b/code/MMDImporter.cpp @@ -118,7 +118,7 @@ void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene, // Get the file-size and validate it, throwing an exception when fails fileStream.seekg(0, fileStream.end); - size_t fileSize = fileStream.tellg(); + size_t fileSize = static_cast(fileStream.tellg()); fileStream.seekg(0, fileStream.beg); if (fileSize < sizeof(pmx::PmxModel)) { diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 12642a961..4ed8f20ff 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -741,7 +741,7 @@ void glTF2Exporter::MergeMeshes() for (unsigned int n = 0; n < mAsset->nodes.Size(); ++n) { Ref node = mAsset->nodes.Get(n); - unsigned int nMeshes = node->meshes.size(); + unsigned int nMeshes = static_cast(node->meshes.size()); //skip if it's 1 or less meshes per node if (nMeshes > 1) { diff --git a/samples/SimpleOpenGL/CMakeLists.txt b/samples/SimpleOpenGL/CMakeLists.txt index 52c994b63..455cbd8ca 100644 --- a/samples/SimpleOpenGL/CMakeLists.txt +++ b/samples/SimpleOpenGL/CMakeLists.txt @@ -16,6 +16,11 @@ IF ( NOT GLUT_FOUND ) ENDIF ( MSVC ) ENDIF ( NOT GLUT_FOUND ) +if ( MSVC ) + ADD_DEFINITIONS( -D_SCL_SECURE_NO_WARNINGS ) + ADD_DEFINITIONS( -D_CRT_SECURE_NO_WARNINGS ) +endif ( MSVC ) + INCLUDE_DIRECTORIES( ${Assimp_SOURCE_DIR}/include ${Assimp_SOURCE_DIR}/code diff --git a/samples/SimpleTexturedOpenGL/CMakeLists.txt b/samples/SimpleTexturedOpenGL/CMakeLists.txt index 6c34acda5..1b206af50 100644 --- a/samples/SimpleTexturedOpenGL/CMakeLists.txt +++ b/samples/SimpleTexturedOpenGL/CMakeLists.txt @@ -11,6 +11,11 @@ IF ( NOT GLUT_FOUND ) ENDIF ( MSVC ) ENDIF ( NOT GLUT_FOUND ) +if ( MSVC ) + ADD_DEFINITIONS( -D_SCL_SECURE_NO_WARNINGS ) + ADD_DEFINITIONS( -D_CRT_SECURE_NO_WARNINGS ) +endif ( MSVC ) + INCLUDE_DIRECTORIES( ${Assimp_SOURCE_DIR}/include ${Assimp_SOURCE_DIR}/code diff --git a/tools/assimp_view/stdafx.h b/tools/assimp_view/stdafx.h index 3c78d385c..35104d4b0 100644 --- a/tools/assimp_view/stdafx.h +++ b/tools/assimp_view/stdafx.h @@ -23,7 +23,6 @@ #define _WIN32_IE 0x0600 // Ändern Sie dies in den geeigneten Wert für andere Versionen von IE. #endif -#define WIN32_LEAN_AND_MEAN // Selten verwendete Teile der Windows-Header nicht einbinden. // Windows-Headerdateien: #include From 539410d033f0606e444601ab2224bb70ce52c9a5 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 24 Sep 2017 19:31:04 +1000 Subject: [PATCH 110/490] Fixed an error when compiling samples under MSVC that was caused by assuming including windows.h would pull in shellapi.h --- .../SimpleTexturedOpenGL/src/model_loading.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp b/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp index 083650f96..df05b56c4 100644 --- a/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp +++ b/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp @@ -13,6 +13,7 @@ // http://nehe.gamedev.net/ // ---------------------------------------------------------------------------- #include +#include #include #include #include @@ -666,7 +667,7 @@ BOOL CreateGLWindow(const char* title, int width, int height, int bits, bool ful PFD_SUPPORT_OPENGL | // Format Must Support OpenGL PFD_DOUBLEBUFFER, // Must Support Double Buffering PFD_TYPE_RGBA, // Request An RGBA Format - bits, // Select Our Color Depth + BYTE(bits), // Select Our Color Depth 0, 0, 0, 0, 0, 0, // Color Bits Ignored 0, // No Alpha Buffer 0, // Shift Bit Ignored From 8dabd76e03016b9efdffbfce2ef2e7d1fa9db403 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 24 Sep 2017 21:19:03 +1000 Subject: [PATCH 111/490] Fixed a warning caused by aiVector3D appearing in a packed struct, causing it to fail to pack as requested. --- code/MDLFileData.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/MDLFileData.h b/code/MDLFileData.h index 2afea8a95..3ef58ca5a 100644 --- a/code/MDLFileData.h +++ b/code/MDLFileData.h @@ -126,16 +126,16 @@ struct Header { int32_t version; //! scale factors for each axis - aiVector3D scale; + ai_real scale[3]; //! translation factors for each axis - aiVector3D translate; + ai_real translate[3]; //! bounding radius of the mesh float boundingradius; //! Position of the viewer's exe. Ignored - aiVector3D vEyePos; + ai_real vEyePos[3]; //! Number of textures int32_t num_skins; From 79a51651068b5b5bef4d7b609a7301d9f6790b89 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 24 Sep 2017 21:46:15 +1000 Subject: [PATCH 112/490] Fixed unused variable warning by replacing them with descriptive comments --- code/FBXBinaryTokenizer.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/code/FBXBinaryTokenizer.cpp b/code/FBXBinaryTokenizer.cpp index 46e85d888..ede32d7b7 100644 --- a/code/FBXBinaryTokenizer.cpp +++ b/code/FBXBinaryTokenizer.cpp @@ -439,11 +439,11 @@ void TokenizeBinary(TokenList& output_tokens, const char* input, unsigned int le } const char* cursor = input + 18; - const uint8_t unknown_1 = ReadByte(input, cursor, input + length); - const uint8_t unknown_2 = ReadByte(input, cursor, input + length); - const uint8_t unknown_3 = ReadByte(input, cursor, input + length); - const uint8_t unknown_4 = ReadByte(input, cursor, input + length); - const uint8_t unknown_5 = ReadByte(input, cursor, input + length); + /*Result ignored*/ ReadByte(input, cursor, input + length); + /*Result ignored*/ ReadByte(input, cursor, input + length); + /*Result ignored*/ ReadByte(input, cursor, input + length); + /*Result ignored*/ ReadByte(input, cursor, input + length); + /*Result ignored*/ ReadByte(input, cursor, input + length); const uint32_t version = ReadWord(input, cursor, input + length); const bool is64bits = version >= 7500; while (cursor < input + length) From 7e91ac34436d8f0708e1b43cbe5b4f2e3885e9a8 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Mon, 25 Sep 2017 20:22:06 +1000 Subject: [PATCH 113/490] Suppressed warning on gcc caused by the 'visibility' attribute being ignored on types. --- include/assimp/scene.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/assimp/scene.h b/include/assimp/scene.h index 4d027456c..342c316d6 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -60,6 +60,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. extern "C" { #endif +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wattributes" +#endif + // ------------------------------------------------------------------------------- /** * A node in the imported hierarchy. @@ -163,6 +168,9 @@ struct ASSIMP_API aiNode #endif // __cplusplus }; +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif // ------------------------------------------------------------------------------- /** From f6fc5a7a117ea41e43b0bdb1426e9b319924d8fd Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Mon, 25 Sep 2017 22:07:01 +1000 Subject: [PATCH 114/490] Changed the FileSizeTest to not rely on tmpnam to eliminate warning on gcc. --- test/unit/utDefaultIOStream.cpp | 47 ++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/test/unit/utDefaultIOStream.cpp b/test/unit/utDefaultIOStream.cpp index 9c1beb193..9ef2bad86 100644 --- a/test/unit/utDefaultIOStream.cpp +++ b/test/unit/utDefaultIOStream.cpp @@ -39,6 +39,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------*/ #include #include "TestIOStream.h" +#include +#include +#include + +#if defined(__GNUC__) || defined(__clang__) +#define MAKE_TEMP_FILE_NAME mktemp +#elif defined(_MSC_VER) +#include +#define MAKE_TEMP_FILE_NAME _mktemp +#endif using namespace ::Assimp; @@ -46,16 +56,33 @@ class utDefaultIOStream : public ::testing::Test { // empty }; -TEST_F( utDefaultIOStream, FileSizeTest ) { - char buffer[ L_tmpnam ]; - tmpnam( buffer ); - std::FILE *fs( std::fopen( buffer, "w+" ) ); - size_t written( std::fwrite( buffer, 1, sizeof( char ) * L_tmpnam, fs ) ); - EXPECT_NE( 0U, written ); - std::fflush( fs ); - TestDefaultIOStream myStream( fs, buffer ); + +const char data[]{"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Qui\ +sque luctus sem diam, ut eleifend arcu auctor eu. Vestibulum id est vel nulla l\ +obortis malesuada ut sed turpis. Nulla a volutpat tortor. Nunc vestibulum portt\ +itor sapien ornare sagittis volutpat."}; + +TEST_F( utDefaultIOStream, FileSizeTest ) { + const auto dataSize = sizeof(data); + const auto dataCount = dataSize / sizeof(*data); + + char fpath[] = { "./rndfp.XXXXXX" }; + auto tmplate = MAKE_TEMP_FILE_NAME(fpath); + ASSERT_NE(tmplate, nullptr); + + //char buffer[ L_tmpnam ]; + //tmpnam( buffer ); + auto *fs = std::fopen(fpath, "w+" ); + ASSERT_NE(fs, nullptr); + auto written = std::fwrite(data, sizeof(*data), dataCount, fs ); + EXPECT_NE( 0U, written ); + auto vflush = std::fflush( fs ); + ASSERT_EQ(vflush, 0); + + TestDefaultIOStream myStream( fs, fpath); size_t size = myStream.FileSize(); - EXPECT_EQ( size, sizeof( char ) * L_tmpnam ); - remove( buffer ); + EXPECT_EQ( size, dataSize); + remove(fpath); + } From 4360267cb22daec5e7e35d4b96d56dac4879feb5 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Mon, 25 Sep 2017 22:58:47 +1000 Subject: [PATCH 115/490] Replaced flakey macros with specific functions to serve the purpose --- test/unit/utDefaultIOStream.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/test/unit/utDefaultIOStream.cpp b/test/unit/utDefaultIOStream.cpp index 9ef2bad86..9335007ba 100644 --- a/test/unit/utDefaultIOStream.cpp +++ b/test/unit/utDefaultIOStream.cpp @@ -44,10 +44,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #if defined(__GNUC__) || defined(__clang__) -#define MAKE_TEMP_FILE_NAME mktemp +#define TMP_PATH "/tmp/" +void MakeTmpFilePath(char* tmplate) +{ + auto err = mkstemp(tmplate); + ASSERT_NE(err, -1); +} #elif defined(_MSC_VER) #include -#define MAKE_TEMP_FILE_NAME _mktemp +#define TMP_PATH "./" +void MakeTmpFilePath(char* tmplate) +{ + auto pathtemplate = _mktemp(tmplate); + ASSERT_NE(pathtemplate, nullptr); +} #endif using namespace ::Assimp; @@ -67,12 +77,9 @@ TEST_F( utDefaultIOStream, FileSizeTest ) { const auto dataSize = sizeof(data); const auto dataCount = dataSize / sizeof(*data); - char fpath[] = { "./rndfp.XXXXXX" }; - auto tmplate = MAKE_TEMP_FILE_NAME(fpath); - ASSERT_NE(tmplate, nullptr); + char fpath[] = { TMP_PATH"rndfp.XXXXXX" }; + MakeTmpFilePath(fpath); - //char buffer[ L_tmpnam ]; - //tmpnam( buffer ); auto *fs = std::fopen(fpath, "w+" ); ASSERT_NE(fs, nullptr); auto written = std::fwrite(data, sizeof(*data), dataCount, fs ); From e2ab3e0d29511d60529fd0ff22bd85be896902fe Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Tue, 26 Sep 2017 21:32:11 +1000 Subject: [PATCH 116/490] Changed the method by which temporary files are created for unit the FileSizeTest. Will apply to other tests next. --- test/unit/utDefaultIOStream.cpp | 70 +++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/test/unit/utDefaultIOStream.cpp b/test/unit/utDefaultIOStream.cpp index 9335007ba..c5f5ae760 100644 --- a/test/unit/utDefaultIOStream.cpp +++ b/test/unit/utDefaultIOStream.cpp @@ -43,30 +43,42 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#if defined(__GNUC__) || defined(__clang__) -#define TMP_PATH "/tmp/" -void MakeTmpFilePath(char* tmplate) -{ - auto err = mkstemp(tmplate); - ASSERT_NE(err, -1); -} -#elif defined(_MSC_VER) -#include -#define TMP_PATH "./" -void MakeTmpFilePath(char* tmplate) -{ - auto pathtemplate = _mktemp(tmplate); - ASSERT_NE(pathtemplate, nullptr); -} -#endif - using namespace ::Assimp; class utDefaultIOStream : public ::testing::Test { // empty }; - +#if defined(__GNUC__) || defined(__clang__) +#define TMP_PATH "/tmp/" +FILE* MakeTmpFilePath(char* tmplate) +{ + auto fd = mkstemp(tmplate); + EXPECT_NE(-1, fd); + if(fd == -1) + { + return nullptr; + } + auto fs = fdopen(fd, "w+"); + EXPECT_NE(nullptr, fs); + return fs; +} +#elif defined(_MSC_VER) +#include +#define TMP_PATH "./" +FILE* MakeTmpFilePath(char* tmplate) +{ + auto pathtemplate = _mktemp(tmplate); + EXPECT_NE(pathtemplate, nullptr); + if(pathtemplate == nullptr) + { + return nullptr; + } + auto* fs = std::fopen(pathtemplate, "w+"); + EXPECT_NE(fs, nullptr); + return fs; +} +#endif const char data[]{"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Qui\ sque luctus sem diam, ut eleifend arcu auctor eu. Vestibulum id est vel nulla l\ @@ -78,18 +90,18 @@ TEST_F( utDefaultIOStream, FileSizeTest ) { const auto dataCount = dataSize / sizeof(*data); char fpath[] = { TMP_PATH"rndfp.XXXXXX" }; - MakeTmpFilePath(fpath); + auto* fs = MakeTmpFilePath(fpath); + ASSERT_NE(nullptr, fs); + { + auto written = std::fwrite(data, sizeof(*data), dataCount, fs ); + EXPECT_NE( 0U, written ); - auto *fs = std::fopen(fpath, "w+" ); - ASSERT_NE(fs, nullptr); - auto written = std::fwrite(data, sizeof(*data), dataCount, fs ); - EXPECT_NE( 0U, written ); - auto vflush = std::fflush( fs ); - ASSERT_EQ(vflush, 0); + auto vflush = std::fflush( fs ); + ASSERT_EQ(vflush, 0); - TestDefaultIOStream myStream( fs, fpath); - size_t size = myStream.FileSize(); - EXPECT_EQ( size, dataSize); + TestDefaultIOStream myStream( fs, fpath); + size_t size = myStream.FileSize(); + EXPECT_EQ( size, dataSize); + } remove(fpath); - } From 980d2b0eee6981625d92bc01dfd24ab06f3068db Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Tue, 26 Sep 2017 22:08:52 +1000 Subject: [PATCH 117/490] Added a header to hold the file generation code for unit testing purposes. --- test/unit/UnitTestFileGenerator.h | 76 +++++++++++++++++++++++++++++++ test/unit/utDefaultIOStream.cpp | 32 +------------ 2 files changed, 77 insertions(+), 31 deletions(-) create mode 100644 test/unit/UnitTestFileGenerator.h diff --git a/test/unit/UnitTestFileGenerator.h b/test/unit/UnitTestFileGenerator.h new file mode 100644 index 000000000..04f5fd409 --- /dev/null +++ b/test/unit/UnitTestFileGenerator.h @@ -0,0 +1,76 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +#pragma once + +#include +#include +#include + +#if defined(__GNUC__) || defined(__clang__) +#define TMP_PATH "/tmp/" +inline FILE* MakeTmpFilePath(char* tmplate) +{ + auto fd = mkstemp(tmplate); + EXPECT_NE(-1, fd); + if(fd == -1) + { + return nullptr; + } + auto fs = fdopen(fd, "w+"); + EXPECT_NE(nullptr, fs); + return fs; +} +#elif defined(_MSC_VER) +#include +#define TMP_PATH "./" +inline FILE* MakeTmpFilePath(char* tmplate) +{ + auto pathtemplate = _mktemp(tmplate); + EXPECT_NE(pathtemplate, nullptr); + if(pathtemplate == nullptr) + { + return nullptr; + } + auto* fs = std::fopen(pathtemplate, "w+"); + EXPECT_NE(fs, nullptr); + return fs; +} +#endif diff --git a/test/unit/utDefaultIOStream.cpp b/test/unit/utDefaultIOStream.cpp index c5f5ae760..1d78fb616 100644 --- a/test/unit/utDefaultIOStream.cpp +++ b/test/unit/utDefaultIOStream.cpp @@ -39,6 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------*/ #include #include "TestIOStream.h" +#include "UnitTestFileGenerator.h" #include #include #include @@ -49,37 +50,6 @@ class utDefaultIOStream : public ::testing::Test { // empty }; -#if defined(__GNUC__) || defined(__clang__) -#define TMP_PATH "/tmp/" -FILE* MakeTmpFilePath(char* tmplate) -{ - auto fd = mkstemp(tmplate); - EXPECT_NE(-1, fd); - if(fd == -1) - { - return nullptr; - } - auto fs = fdopen(fd, "w+"); - EXPECT_NE(nullptr, fs); - return fs; -} -#elif defined(_MSC_VER) -#include -#define TMP_PATH "./" -FILE* MakeTmpFilePath(char* tmplate) -{ - auto pathtemplate = _mktemp(tmplate); - EXPECT_NE(pathtemplate, nullptr); - if(pathtemplate == nullptr) - { - return nullptr; - } - auto* fs = std::fopen(pathtemplate, "w+"); - EXPECT_NE(fs, nullptr); - return fs; -} -#endif - const char data[]{"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Qui\ sque luctus sem diam, ut eleifend arcu auctor eu. Vestibulum id est vel nulla l\ obortis malesuada ut sed turpis. Nulla a volutpat tortor. Nunc vestibulum portt\ From e77e89c8b7dd6391b300262670b884a407021a3b Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Tue, 26 Sep 2017 22:41:20 +1000 Subject: [PATCH 118/490] Improved the naming of temporary file generator function. Replaced use of tmpnam in utIOStreamBuffer with this facility to addresssafety warning. --- test/unit/UnitTestFileGenerator.h | 4 +-- test/unit/utDefaultIOStream.cpp | 2 +- test/unit/utIOStreamBuffer.cpp | 56 +++++++++++++++++++++---------- 3 files changed, 41 insertions(+), 21 deletions(-) diff --git a/test/unit/UnitTestFileGenerator.h b/test/unit/UnitTestFileGenerator.h index 04f5fd409..2e4aede0f 100644 --- a/test/unit/UnitTestFileGenerator.h +++ b/test/unit/UnitTestFileGenerator.h @@ -46,7 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #if defined(__GNUC__) || defined(__clang__) #define TMP_PATH "/tmp/" -inline FILE* MakeTmpFilePath(char* tmplate) +inline FILE* MakeTmpFile(char* tmplate) { auto fd = mkstemp(tmplate); EXPECT_NE(-1, fd); @@ -61,7 +61,7 @@ inline FILE* MakeTmpFilePath(char* tmplate) #elif defined(_MSC_VER) #include #define TMP_PATH "./" -inline FILE* MakeTmpFilePath(char* tmplate) +inline FILE* MakeTmpFile(char* tmplate) { auto pathtemplate = _mktemp(tmplate); EXPECT_NE(pathtemplate, nullptr); diff --git a/test/unit/utDefaultIOStream.cpp b/test/unit/utDefaultIOStream.cpp index 1d78fb616..c0d82b6dc 100644 --- a/test/unit/utDefaultIOStream.cpp +++ b/test/unit/utDefaultIOStream.cpp @@ -60,7 +60,7 @@ TEST_F( utDefaultIOStream, FileSizeTest ) { const auto dataCount = dataSize / sizeof(*data); char fpath[] = { TMP_PATH"rndfp.XXXXXX" }; - auto* fs = MakeTmpFilePath(fpath); + auto* fs = MakeTmpFile(fpath); ASSERT_NE(nullptr, fs); { auto written = std::fwrite(data, sizeof(*data), dataCount, fs ); diff --git a/test/unit/utIOStreamBuffer.cpp b/test/unit/utIOStreamBuffer.cpp index 0bf69bf00..a1b67da44 100644 --- a/test/unit/utIOStreamBuffer.cpp +++ b/test/unit/utIOStreamBuffer.cpp @@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "UnitTestPCH.h" #include "IOStreamBuffer.h" #include "TestIOStream.h" +#include "UnitTestFileGenerator.h" class IOStreamBufferTest : public ::testing::Test { // empty @@ -68,40 +69,59 @@ TEST_F( IOStreamBufferTest, accessCacheSizeTest ) { EXPECT_EQ( 100U, myBuffer2.cacheSize() ); } +const char data[]{"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Qui\ +sque luctus sem diam, ut eleifend arcu auctor eu. Vestibulum id est vel nulla l\ +obortis malesuada ut sed turpis. Nulla a volutpat tortor. Nunc vestibulum portt\ +itor sapien ornare sagittis volutpat."}; + + TEST_F( IOStreamBufferTest, open_close_Test ) { IOStreamBuffer myBuffer; EXPECT_FALSE( myBuffer.open( nullptr ) ); EXPECT_FALSE( myBuffer.close() ); + + const auto dataSize = sizeof(data); + const auto dataCount = dataSize / sizeof(*data); - char buffer[ L_tmpnam ]; - tmpnam( buffer ); - std::FILE *fs( std::fopen( buffer, "w+" ) ); - size_t written( std::fwrite( buffer, 1, sizeof( char ) * L_tmpnam, fs ) ); + char fname[]={ "octest.XXXXXX" }; + auto* fs = MakeTmpFile(fname); + ASSERT_NE(nullptr, fs); + + auto written = std::fwrite( data, sizeof(*data), dataCount, fs ); EXPECT_NE( 0U, written ); std::fflush( fs ); + { + TestDefaultIOStream myStream( fs, fname ); - TestDefaultIOStream myStream( fs, buffer ); - - EXPECT_TRUE( myBuffer.open( &myStream ) ); - EXPECT_FALSE( myBuffer.open( &myStream ) ); - EXPECT_TRUE( myBuffer.close() ); + EXPECT_TRUE( myBuffer.open( &myStream ) ); + EXPECT_FALSE( myBuffer.open( &myStream ) ); + EXPECT_TRUE( myBuffer.close() ); + } + remove(fname); } TEST_F( IOStreamBufferTest, readlineTest ) { - char buffer[ L_tmpnam ]; - tmpnam( buffer ); - std::FILE *fs( std::fopen( buffer, "w+" ) ); - size_t written( std::fwrite( buffer, 1, sizeof( char ) * L_tmpnam, fs ) ); + + const auto dataSize = sizeof(data); + const auto dataCount = dataSize / sizeof(*data); + + char fname[]={ "readlinetest.XXXXXX" }; + auto* fs = MakeTmpFile(fname); + ASSERT_NE(nullptr, fs); + + auto written = std::fwrite( data, sizeof(*data), dataCount, fs ); EXPECT_NE( 0U, written ); std::fflush( fs ); - IOStreamBuffer myBuffer( 26 ); - EXPECT_EQ( 26U, myBuffer.cacheSize() ); + const auto tCacheSize = 26u; - TestDefaultIOStream myStream( fs, buffer ); - size_t size( myStream.FileSize() ); - size_t numBlocks( size / myBuffer.cacheSize() ); + IOStreamBuffer myBuffer( tCacheSize ); + EXPECT_EQ(tCacheSize, myBuffer.cacheSize() ); + + TestDefaultIOStream myStream( fs, fname ); + auto size = myStream.FileSize(); + auto numBlocks = size / myBuffer.cacheSize(); if ( size % myBuffer.cacheSize() > 0 ) { numBlocks++; } From febd611d48c064f74341b42906229bd1a57a8e39 Mon Sep 17 00:00:00 2001 From: Josh Faust Date: Wed, 27 Sep 2017 18:41:35 -0700 Subject: [PATCH 119/490] Fix glTF2::Asset::FindUniqueID() when the input string is >= 256 chars --- code/glTF2Asset.inl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 47904b38f..264a32d4d 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -1118,11 +1118,12 @@ inline std::string Asset::FindUniqueID(const std::string& str, const char* suffi if (it == mUsedIds.end()) return id; - char buffer[256]; - int offset = ai_snprintf(buffer, sizeof(buffer), "%s_", id.c_str()); + std::vector buffer; + buffer.resize(id.size() + 16); + int offset = ai_snprintf(buffer.data(), buffer.size(), "%s_", id.c_str()); for (int i = 0; it != mUsedIds.end(); ++i) { - ai_snprintf(buffer + offset, sizeof(buffer) - offset, "%d", i); - id = buffer; + ai_snprintf(buffer.data() + offset, buffer.size() - offset, "%d", i); + id = buffer.data(); it = mUsedIds.find(id); } From 12b6895f7b682255f60dcd77dd26a75da63a4697 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Fri, 29 Sep 2017 01:29:11 +1000 Subject: [PATCH 120/490] Replaced unsigned long for the crc table to z_crc_t, to match what is returned by get-crc_table --- contrib/unzip/crypt.h | 2 +- contrib/unzip/unzip.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/unzip/crypt.h b/contrib/unzip/crypt.h index 622f4bc2e..5fd3dcd6c 100644 --- a/contrib/unzip/crypt.h +++ b/contrib/unzip/crypt.h @@ -45,7 +45,7 @@ static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) /*********************************************************************** * Update the encryption keys with the next byte of plain text */ -static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c) +static int update_keys(unsigned long* pkeys,const z_crc_t* pcrc_32_tab,int c) { (*(pkeys+0)) = CRC32((*(pkeys+0)), c); (*(pkeys+1)) += (*(pkeys+0)) & 0xff; diff --git a/contrib/unzip/unzip.c b/contrib/unzip/unzip.c index 9ad4766d8..e9ad1ff99 100644 --- a/contrib/unzip/unzip.c +++ b/contrib/unzip/unzip.c @@ -147,7 +147,7 @@ typedef struct int encrypted; # ifndef NOUNCRYPT unsigned long keys[3]; /* keys defining the pseudo-random sequence */ - const unsigned long* pcrc_32_tab; + const z_crc_t* pcrc_32_tab; # endif } unz_s; From 4feac1b1f93c8f12df65b1592d64b06e6a048fc6 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Fri, 29 Sep 2017 01:40:05 +1000 Subject: [PATCH 121/490] Changed a couple more function interfaces to correct the input type. --- contrib/unzip/crypt.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/unzip/crypt.h b/contrib/unzip/crypt.h index 5fd3dcd6c..2aa99c284 100644 --- a/contrib/unzip/crypt.h +++ b/contrib/unzip/crypt.h @@ -32,7 +32,7 @@ /*********************************************************************** * Return the next byte in the pseudo-random sequence */ -static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) +static int decrypt_byte(unsigned long* pkeys, const z_crc_t* pcrc_32_tab) { unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an * unpredictable manner on 16-bit systems; not a problem @@ -62,7 +62,7 @@ static int update_keys(unsigned long* pkeys,const z_crc_t* pcrc_32_tab,int c) * Initialize the encryption keys and the random header according to * the given password. */ -static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab) +static void init_keys(const char* passwd,unsigned long* pkeys,const z_crc_t* pcrc_32_tab) { *(pkeys+0) = 305419896L; *(pkeys+1) = 591751049L; From c42589460d94d0e6334fbb6791309bd1218f5913 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 29 Sep 2017 21:58:58 +0200 Subject: [PATCH 122/490] closes https://github.com/assimp/assimp/issues/1459: fix out-of-boundary access error --- code/SIBImporter.cpp | 53 +++++++++++++++++++------------------------- code/StreamReader.h | 8 ++----- 2 files changed, 25 insertions(+), 36 deletions(-) diff --git a/code/SIBImporter.cpp b/code/SIBImporter.cpp index 4ae2ab7d4..bdda3c677 100644 --- a/code/SIBImporter.cpp +++ b/code/SIBImporter.cpp @@ -176,36 +176,29 @@ static void UnknownChunk(StreamReaderLE* stream, const SIBChunk& chunk) } // Reads a UTF-16LE string and returns it at UTF-8. -static aiString ReadString(StreamReaderLE* stream, uint32_t numWChars) -{ - if ( 0 == numWChars ) { +static aiString ReadString(StreamReaderLE *stream, uint32_t numWChars) { + if ( nullptr == stream || 0 == numWChars ) { static const aiString empty; return empty; } + // Allocate buffers (max expansion is 1 byte -> 4 bytes for UTF-8) - //UTF16* temp = new UTF16[numWChars]; std::vector str; - str.reserve(numWChars * 4 + 1); - //unsigned char* str = new unsigned char[numWChars * 4 + 1]; - uint16_t *temp = new uint16_t[numWChars]; - for (uint32_t n=0;nGetU2(); + str.reserve( numWChars * 4 + 1 ); + uint16_t *temp = new uint16_t[ numWChars ]; + for ( uint32_t n = 0; n < numWChars; ++n ) { + temp[ n ] = stream->GetU2(); + } // Convert it and NUL-terminate. - //const UTF16 *start = temp, *end = temp + numWChars; + const uint16_t *start( temp ), *end( temp + numWChars ); + utf8::utf16to8( start, end, back_inserter( str ) ); + str[ str.size() - 1 ] = '\0'; - const uint16_t *start = temp, *end = temp + numWChars; - utf8::utf16to8(start, end, back_inserter(str)); - - //UTF8 *dest = str, *limit = str + numWChars*4; - //ConvertUTF16toUTF8(&start, end, &dest, limit, lenientConversion); - //*dest = '\0'; - - str[str.size()] = '\0'; // Return the final string. aiString result = aiString((const char *)&str[0]); - //delete[] str; delete[] temp; + return result; } @@ -223,26 +216,26 @@ SIBImporter::~SIBImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool SIBImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const -{ +bool SIBImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const { return SimpleExtensionCheck(pFile, "sib"); } // ------------------------------------------------------------------------------------------------ -const aiImporterDesc* SIBImporter::GetInfo () const -{ +const aiImporterDesc* SIBImporter::GetInfo () const { return &desc; } // ------------------------------------------------------------------------------------------------ -static void ReadVerts(SIBMesh* mesh, StreamReaderLE* stream, uint32_t count) -{ - mesh->pos.resize(count); +static void ReadVerts(SIBMesh* mesh, StreamReaderLE* stream, uint32_t count) { + if ( nullptr == mesh || nullptr == stream ) { + return; + } - for (uint32_t n=0;npos[n].x = stream->GetF4(); - mesh->pos[n].y = stream->GetF4(); - mesh->pos[n].z = stream->GetF4(); + mesh->pos.resize(count); + for ( uint32_t n=0; npos[ n ].x = stream->GetF4(); + mesh->pos[ n ].y = stream->GetF4(); + mesh->pos[ n ].z = stream->GetF4(); } } diff --git a/code/StreamReader.h b/code/StreamReader.h index 494aed146..6220de9a8 100644 --- a/code/StreamReader.h +++ b/code/StreamReader.h @@ -291,15 +291,11 @@ private: throw DeadlyImportError("End of file or stream limit was reached"); } -///*#ifdef __arm__ T f; ::memcpy (&f, current, sizeof(T)); -//#else*/ -// T f = *((const T*)current); -//#endif - Intern :: Getter() (&f,le); - + Intern::Getter() (&f,le); current += sizeof(T); + return f; } From 90330712374c20b870cbf7915ace158fe0d069bb Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 30 Sep 2017 09:37:34 +0200 Subject: [PATCH 123/490] Obj: rename attribute from exporter. --- code/ObjExporter.cpp | 44 +++++++++++++++++++++++++------------------- code/ObjExporter.h | 6 +++--- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/code/ObjExporter.cpp b/code/ObjExporter.cpp index 14035c262..cc9d2f31c 100644 --- a/code/ObjExporter.cpp +++ b/code/ObjExporter.cpp @@ -91,11 +91,11 @@ ObjExporter::ObjExporter(const char* _filename, const aiScene* pScene) , vn() , vt() , vc() -, vpMap() -, vnMap() -, vtMap() -, vcMap() -, meshes() +, mVpMap() +, mVnMap() +, mVtMap() +, mVcMap() +, mMeshes() , endl("\n") { // make sure that all formatting happens using the standard, C locale and not the user's current locale const std::locale& l = std::locale("C"); @@ -137,15 +137,21 @@ std::string ObjExporter::GetMaterialLibFileName() { } // ------------------------------------------------------------------------------------------------ -void ObjExporter :: WriteHeader(std::ostringstream& out) { +void ObjExporter::WriteHeader(std::ostringstream& out) { out << "# File produced by Open Asset Import Library (http://www.assimp.sf.net)" << endl; - out << "# (assimp v" << aiGetVersionMajor() << '.' << aiGetVersionMinor() << '.' << aiGetVersionRevision() << ")" << endl << endl; + out << "# (assimp v" << aiGetVersionMajor() << '.' << aiGetVersionMinor() << '.' + << aiGetVersionRevision() << ")" << endl << endl; } // ------------------------------------------------------------------------------------------------ -std::string ObjExporter :: GetMaterialName(unsigned int index) +std::string ObjExporter::GetMaterialName(unsigned int index) { const aiMaterial* const mat = pScene->mMaterials[index]; + if ( nullptr == mat ) { + static const std::string EmptyStr; + return EmptyStr; + } + aiString s; if(AI_SUCCESS == mat->Get(AI_MATKEY_NAME,s)) { return std::string(s.data,s.length); @@ -235,8 +241,8 @@ void ObjExporter::WriteGeometryFile() { AddNode(pScene->mRootNode, mBase); // write vertex positions with colors, if any - vpMap.getVectors( vp ); - vcMap.getColors( vc ); + mVpMap.getVectors( vp ); + mVcMap.getColors( vc ); if ( vc.empty() ) { mOutput << "# " << vp.size() << " vertex positions" << endl; for ( const aiVector3D& v : vp ) { @@ -253,7 +259,7 @@ void ObjExporter::WriteGeometryFile() { mOutput << endl; // write uv coordinates - vtMap.getVectors(vt); + mVtMap.getVectors(vt); mOutput << "# " << vt.size() << " UV coordinates" << endl; for(const aiVector3D& v : vt) { mOutput << "vt " << v.x << " " << v.y << " " << v.z << endl; @@ -261,7 +267,7 @@ void ObjExporter::WriteGeometryFile() { mOutput << endl; // write vertex normals - vnMap.getVectors(vn); + mVnMap.getVectors(vn); mOutput << "# " << vn.size() << " vertex normals" << endl; for(const aiVector3D& v : vn) { mOutput << "vn " << v.x << " " << v.y << " " << v.z << endl; @@ -269,7 +275,7 @@ void ObjExporter::WriteGeometryFile() { mOutput << endl; // now write all mesh instances - for(const MeshInstance& m : meshes) { + for(const MeshInstance& m : mMeshes) { mOutput << "# Mesh \'" << m.name << "\' with " << m.faces.size() << " faces" << endl; if (!m.name.empty()) { mOutput << "g " << m.name << endl; @@ -345,8 +351,8 @@ void ObjExporter::colIndexMap::getColors( std::vector &colors ) { // ------------------------------------------------------------------------------------------------ void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat) { - meshes.push_back(MeshInstance()); - MeshInstance& mesh = meshes.back(); + mMeshes.push_back(MeshInstance()); + MeshInstance& mesh = mMeshes.back(); mesh.name = std::string(name.data,name.length) + (m->mName.length ? "_" + std::string(m->mName.data,m->mName.length) : ""); mesh.matname = GetMaterialName(m->mMaterialIndex); @@ -373,24 +379,24 @@ void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4 const unsigned int idx = f.mIndices[a]; aiVector3D vert = mat * m->mVertices[idx]; - face.indices[a].vp = vpMap.getIndex(vert); + face.indices[a].vp = mVpMap.getIndex(vert); if (m->mNormals) { aiVector3D norm = aiMatrix3x3(mat) * m->mNormals[idx]; - face.indices[a].vn = vnMap.getIndex(norm); + face.indices[a].vn = mVnMap.getIndex(norm); } else { face.indices[a].vn = 0; } if ( nullptr != m->mColors[ 0 ] ) { aiColor4D col4 = m->mColors[ 0 ][ idx ]; - face.indices[ a ].vc = vcMap.getIndex( col4 ); + face.indices[ a ].vc = mVcMap.getIndex( col4 ); } else { face.indices[ a ].vc = 0; } if ( m->mTextureCoords[ 0 ] ) { - face.indices[a].vt = vtMap.getIndex(m->mTextureCoords[0][idx]); + face.indices[a].vt = mVtMap.getIndex(m->mTextureCoords[0][idx]); } else { face.indices[a].vt = 0; } diff --git a/code/ObjExporter.h b/code/ObjExporter.h index 0ba042b0b..fb60402d7 100644 --- a/code/ObjExporter.h +++ b/code/ObjExporter.h @@ -163,9 +163,9 @@ private: void getColors( std::vector &colors ); }; - vecIndexMap vpMap, vnMap, vtMap; - colIndexMap vcMap; - std::vector meshes; + vecIndexMap mVpMap, mVnMap, mVtMap; + colIndexMap mVcMap; + std::vector mMeshes; // this endl() doesn't flush() the stream const std::string endl; From 1c76962c980e42e45eeb0f255162e1dc08740789 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 30 Sep 2017 09:38:13 +0200 Subject: [PATCH 124/490] closes https://github.com/assimp/assimp/issues/1450: use correct name of exporter to gltf2 --- code/Exporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 7af7d2cf5..1e2d7cbf1 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -145,7 +145,7 @@ Exporter::ExportFormatEntry gExporters[] = aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType), Exporter::ExportFormatEntry( "glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB, aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType), - Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf", &ExportSceneGLTF2, + Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf2", &ExportSceneGLTF2, aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType), #endif From 2056e56bdbe5cf4316c7cc5d48008d1199a8142f Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 30 Sep 2017 10:45:14 +0200 Subject: [PATCH 125/490] Obj: prepare test to reproduce crash on linux. --- test/unit/utObjImportExport.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/unit/utObjImportExport.cpp b/test/unit/utObjImportExport.cpp index d4d4fbf9e..94bc71f67 100644 --- a/test/unit/utObjImportExport.cpp +++ b/test/unit/utObjImportExport.cpp @@ -254,3 +254,18 @@ TEST_F( utObjImportExport, issue809_vertex_color_Test ) { EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "obj", ASSIMP_TEST_MODELS_DIR "/OBJ/test.obj" ) ); #endif // ASSIMP_BUILD_NO_EXPORT } + +TEST_F( utObjImportExport, issue1453_segfault ) { + static const std::string ObjModel = + "v 0.0 0.0 0.0" + "v 0.0 0.0 1.0" + "v 0.0 1.0 0.0" + "v 0.0 1.0 1.0" + "v 1.0 0.0 0.0" + "v 1.0 0.0 1.0" + "v 1.0 1.0 0.0" + "v 1.0 1.0 1.0"; + + Assimp::Importer myimporter; + const aiScene* myscene = myimporter.ReadFileFromMemory( ObjModel.c_str(), ObjModel.size(), 0 ); +} From c4e91eb33f811a50c38e89dff79f393a38164aea Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 30 Sep 2017 10:47:23 +0200 Subject: [PATCH 126/490] add some asserts. --- code/AssbinLoader.h | 1 - code/FBXConverter.cpp | 2 +- code/FBXProperties.h | 20 +++++++------------- code/SMDLoader.cpp | 6 ++++-- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/code/AssbinLoader.h b/code/AssbinLoader.h index fc28d1667..2eb7a6488 100644 --- a/code/AssbinLoader.h +++ b/code/AssbinLoader.h @@ -71,7 +71,6 @@ class AssbinImporter : public BaseImporter private: bool shortened; bool compressed; -protected: public: virtual bool CanRead( diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index 850028ae8..1a80fa4f5 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -1096,7 +1096,7 @@ void Converter::SetupNodeMetadata( const Model& model, aiNode& nd ) DirectPropertyMap unparsedProperties = props.GetUnparsedProperties(); // create metadata on node - std::size_t numStaticMetaData = 2; + const std::size_t numStaticMetaData = 2; aiMetadata* data = aiMetadata::Alloc( static_cast(unparsedProperties.size() + numStaticMetaData) ); nd.mMetaData = data; int index = 0; diff --git a/code/FBXProperties.h b/code/FBXProperties.h index 91b7c8128..09b8aa94c 100644 --- a/code/FBXProperties.h +++ b/code/FBXProperties.h @@ -62,8 +62,7 @@ class Element; P: "ShininessExponent", "double", "Number", "",0.5 @endvebatim */ -class Property -{ +class Property { protected: Property(); @@ -78,15 +77,13 @@ public: }; template -class TypedProperty : public Property -{ +class TypedProperty : public Property { public: explicit TypedProperty(const T& value) - : value(value) - { + : value(value) { + // empty } -public: const T& Value() const { return value; } @@ -97,21 +94,19 @@ private: typedef std::fbx_unordered_map > DirectPropertyMap; -typedef std::fbx_unordered_map PropertyMap; -typedef std::fbx_unordered_map LazyPropertyMap; +typedef std::fbx_unordered_map PropertyMap; +typedef std::fbx_unordered_map LazyPropertyMap; /** * Represents a property table as can be found in the newer FBX files (Properties60, Properties70) */ -class PropertyTable -{ +class PropertyTable { public: // in-memory property table with no source element PropertyTable(); PropertyTable(const Element& element, std::shared_ptr templateProps); ~PropertyTable(); -public: const Property* Get(const std::string& name) const; // PropertyTable's need not be coupled with FBX elements so this can be NULL @@ -132,7 +127,6 @@ private: const Element* const element; }; - // ------------------------------------------------------------------------------------------------ template inline diff --git a/code/SMDLoader.cpp b/code/SMDLoader.cpp index 4502e8d7a..60e3f63c8 100644 --- a/code/SMDLoader.cpp +++ b/code/SMDLoader.cpp @@ -650,12 +650,14 @@ void SMDImporter::ComputeAbsoluteBoneTransformations() // create output materials void SMDImporter::CreateOutputMaterials() { + ai_assert( nullptr != pScene ); + pScene->mNumMaterials = (unsigned int)aszTextures.size(); pScene->mMaterials = new aiMaterial*[std::max(1u, pScene->mNumMaterials)]; - for (unsigned int iMat = 0; iMat < pScene->mNumMaterials;++iMat) - { + for (unsigned int iMat = 0; iMat < pScene->mNumMaterials; ++iMat) { aiMaterial* pcMat = new aiMaterial(); + ai_assert( nullptr != pcMat ); pScene->mMaterials[iMat] = pcMat; aiString szName; From a9e883627116e00c4fb273e79b838f5100dd75d1 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sat, 30 Sep 2017 23:28:02 +1000 Subject: [PATCH 127/490] Added -fPIC flag to C compilers for GCC and clang. Removed -pedantic flag from some compilers. --- CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b404e6916..a33d1431f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -204,9 +204,11 @@ ELSEIF(MSVC) # enable multi-core compilation with MSVC add_compile_options(/MP) ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" ) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fvisibility=hidden -fPIC -Wall -Wno-long-long -pedantic -std=c++11" ) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fvisibility=hidden -fPIC -Wall -Wno-long-long -std=c++11" ) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") ELSEIF( CMAKE_COMPILER_IS_MINGW ) - SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -Wall -Wno-long-long -pedantic -std=c++11" ) + SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -Wall -Wno-long-long -std=c++11" ) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") ADD_DEFINITIONS( -U__STRICT_ANSI__ ) ENDIF() From 9088deeb1d7aab0de902f003a4acf042dcda9b27 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sat, 30 Sep 2017 23:29:56 +1000 Subject: [PATCH 128/490] Eliminated all warnings under clang with default settings. One remains in the included zlib contrib project. --- code/MMDPmxParser.cpp | 18 +++++++++--------- code/glTFAsset.h | 4 +++- contrib/unzip/unzip.c | 21 +++++++++++++++++++-- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/code/MMDPmxParser.cpp b/code/MMDPmxParser.cpp index c38bdf49a..3af23529a 100644 --- a/code/MMDPmxParser.cpp +++ b/code/MMDPmxParser.cpp @@ -216,8 +216,8 @@ namespace pmx void PmxMaterial::Read(std::istream *stream, PmxSetting *setting) { - this->material_name = std::move(ReadString(stream, setting->encoding)); - this->material_english_name = std::move(ReadString(stream, setting->encoding)); + this->material_name = ReadString(stream, setting->encoding); + this->material_english_name = ReadString(stream, setting->encoding); stream->read((char*) this->diffuse, sizeof(float) * 4); stream->read((char*) this->specular, sizeof(float) * 3); stream->read((char*) &this->specularlity, sizeof(float)); @@ -236,7 +236,7 @@ namespace pmx else { this->toon_texture_index = ReadIndex(stream, setting->texture_index_size); } - this->memo = std::move(ReadString(stream, setting->encoding)); + this->memo = ReadString(stream, setting->encoding); stream->read((char*) &this->index_count, sizeof(int)); } @@ -253,8 +253,8 @@ namespace pmx void PmxBone::Read(std::istream *stream, PmxSetting *setting) { - this->bone_name = std::move(ReadString(stream, setting->encoding)); - this->bone_english_name = std::move(ReadString(stream, setting->encoding)); + this->bone_name = ReadString(stream, setting->encoding); + this->bone_english_name = ReadString(stream, setting->encoding); stream->read((char*) this->position, sizeof(float) * 3); this->parent_index = ReadIndex(stream, setting->bone_index_size); stream->read((char*) &this->level, sizeof(int)); @@ -528,10 +528,10 @@ namespace pmx this->setting.Read(stream); // モデル情報 - this->model_name = std::move(ReadString(stream, setting.encoding)); - this->model_english_name = std::move(ReadString(stream, setting.encoding)); - this->model_comment = std::move(ReadString(stream, setting.encoding)); - this->model_english_comment = std::move(ReadString(stream, setting.encoding)); + this->model_name = ReadString(stream, setting.encoding); + this->model_english_name = ReadString(stream, setting.encoding); + this->model_comment = ReadString(stream, setting.encoding); + this->model_english_comment = ReadString(stream, setting.encoding); // 頂点 stream->read((char*) &vertex_count, sizeof(int)); diff --git a/code/glTFAsset.h b/code/glTFAsset.h index 60107bee6..e018e5ace 100644 --- a/code/glTFAsset.h +++ b/code/glTFAsset.h @@ -232,7 +232,9 @@ namespace glTF case ComponentType_UNSIGNED_BYTE: return 1; default: - throw DeadlyImportError("GLTF: Unsupported Component Type "+t); + std::string err = "GLTF: Unsupported Component Type "; + err += t; + throw DeadlyImportError(err); } } diff --git a/contrib/unzip/unzip.c b/contrib/unzip/unzip.c index e9ad1ff99..085d79a02 100644 --- a/contrib/unzip/unzip.c +++ b/contrib/unzip/unzip.c @@ -608,10 +608,16 @@ local int unzlocal_GetCurrentFileInfoInternal (file, /* we check the magic */ if (err==UNZ_OK) + { if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + { err=UNZ_ERRNO; + } else if (uMagic!=0x02014b50) + { err=UNZ_BADZIPFILE; + } + } if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) err=UNZ_ERRNO; @@ -688,18 +694,21 @@ local int unzlocal_GetCurrentFileInfoInternal (file, uSizeRead = extraFieldBufferSize; if (lSeek!=0) + { if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; + } if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; lSeek += file_info.size_file_extra - uSizeRead; } else + { lSeek+=file_info.size_file_extra; - + } if ((err==UNZ_OK) && (szComment!=NULL)) { @@ -710,20 +719,26 @@ local int unzlocal_GetCurrentFileInfoInternal (file, uSizeRead = file_info.size_file_comment; } else + { uSizeRead = commentBufferSize; + } if (lSeek!=0) + { if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; + } if ((file_info.size_file_comment>0) && (commentBufferSize>0)) if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; lSeek+=file_info.size_file_comment - uSizeRead; } else + { lSeek+=file_info.size_file_comment; + } if ((err==UNZ_OK) && (pfile_info!=NULL)) *pfile_info=file_info; @@ -977,10 +992,12 @@ local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, if (err==UNZ_OK) + { if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x04034b50) err=UNZ_BADZIPFILE; + } if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) err=UNZ_ERRNO; @@ -1239,7 +1256,7 @@ extern int ZEXPORT unzReadCurrentFile (file, buf, len) return UNZ_PARAMERROR; - if ((pfile_in_zip_read_info->read_buffer == NULL)) + if (pfile_in_zip_read_info->read_buffer == NULL) return UNZ_END_OF_LIST_OF_FILE; if (len==0) return 0; From 45d93701f898343e804e61e2c03198fba27cd7ff Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 26 Sep 2017 18:33:00 +0300 Subject: [PATCH 129/490] Open3DGC: Fix some uninitialized variable warnings --- contrib/Open3DGC/o3dgcSC3DMCDecoder.inl | 2 +- contrib/Open3DGC/o3dgcSC3DMCEncoder.inl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/Open3DGC/o3dgcSC3DMCDecoder.inl b/contrib/Open3DGC/o3dgcSC3DMCDecoder.inl index d36b62f2b..aa6f24b73 100644 --- a/contrib/Open3DGC/o3dgcSC3DMCDecoder.inl +++ b/contrib/Open3DGC/o3dgcSC3DMCDecoder.inl @@ -425,7 +425,7 @@ namespace o3dgc const AdjacencyInfo & v2T = m_triangleListDecoder.GetVertexToTriangle(); const T * const triangles = ifs.GetCoordIndex(); Vec3 p1, p2, p3, n0, nt; - long na0, nb0; + long na0 = 0, nb0 = 0; Real rna0, rnb0, norm0; char ni0 = 0, ni1 = 0; long a, b, c; diff --git a/contrib/Open3DGC/o3dgcSC3DMCEncoder.inl b/contrib/Open3DGC/o3dgcSC3DMCEncoder.inl index b2c438814..2d30b05a2 100644 --- a/contrib/Open3DGC/o3dgcSC3DMCEncoder.inl +++ b/contrib/Open3DGC/o3dgcSC3DMCEncoder.inl @@ -762,8 +762,8 @@ namespace o3dgc const Real * const originalNormals = ifs.GetNormal(); Vec3 p1, p2, p3, n0, nt; Vec3 n1; - long na0, nb0; - Real rna0, rnb0, na1, nb1, norm0, norm1; + long na0 = 0, nb0 = 0; + Real rna0, rnb0, na1 = 0, nb1 = 0, norm0, norm1; char ni0 = 0, ni1 = 0; long a, b, c, v; m_predictors.Clear(); From 77ce6e562d22d042cd79b60f73bf885e711dcc41 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 30 Sep 2017 16:35:11 +0300 Subject: [PATCH 130/490] Fix CMAKE option name --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b404e6916..8b16c278d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,7 +78,7 @@ OPTION ( ASSIMP_COVERALLS "Enable this to measure test coverage." OFF ) -OPTION ( ASSIMP_WERRRO +OPTION ( ASSIMP_WERROR "Treat warnings as errors." OFF ) From f7b2380f864ff620ab7e4ddafb24715e5e99c244 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 30 Sep 2017 16:35:41 +0300 Subject: [PATCH 131/490] travis: Treat warnings as errors --- .travis.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.sh b/.travis.sh index 7161fd28d..2b09da35e 100755 --- a/.travis.sh +++ b/.travis.sh @@ -1,6 +1,6 @@ function generate() { - cmake -G "Unix Makefiles" -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -DBUILD_SHARED_LIBS=$SHARED_BUILD -DASSIMP_COVERALLS=$ENABLE_COVERALLS + cmake -G "Unix Makefiles" -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -DBUILD_SHARED_LIBS=$SHARED_BUILD -DASSIMP_COVERALLS=$ENABLE_COVERALLS -DASSIMP_ERROR=ON } if [ $ANDROID ]; then From 3f4663e369bf64a989fb386301574a8b6ce51cee Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 2 Oct 2017 09:33:51 +0200 Subject: [PATCH 132/490] closes https://github.com/assimp/assimp/issues/1467. --- code/OpenGEXImporter.cpp | 18 ++++++++++++++---- code/OpenGEXImporter.h | 1 + 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 93c0169d2..7c4d0f982 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -261,6 +261,7 @@ OpenGEXImporter::OpenGEXImporter() , m_nodeChildMap() , m_meshCache() , m_mesh2refMap() +, m_material2refMap() , m_ctx( nullptr ) , m_metrics() , m_currentNode( nullptr ) @@ -482,7 +483,10 @@ void OpenGEXImporter::handleNameNode( DDLNode *node, aiScene *pScene ) { || m_tokenType == Grammar::CameraNodeToken ) { m_currentNode->mName.Set( name.c_str() ); } else if( m_tokenType == Grammar::MaterialToken ) { - + aiString aiName; + aiName.Set( name ); + m_currentMaterial->AddProperty( &aiName, AI_MATKEY_NAME ); + m_material2refMap[ name ] = m_materialCache.size() - 1; } } } @@ -1050,7 +1054,6 @@ void OpenGEXImporter::handleTextureNode( ODDLParser::DDLNode *node, aiScene *pSc m_currentMaterial->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE( 0 ) ); } else if( prop->m_value->getString() == Grammar::SpecularPowerTextureToken ) { m_currentMaterial->AddProperty( &tex, AI_MATKEY_TEXTURE_SPECULAR( 0 ) ); - } else if( prop->m_value->getString() == Grammar::EmissionTextureToken ) { m_currentMaterial->AddProperty( &tex, AI_MATKEY_TEXTURE_EMISSIVE( 0 ) ); } else if( prop->m_value->getString() == Grammar::OpacyTextureToken ) { @@ -1166,7 +1169,7 @@ void OpenGEXImporter::resolveReferences() { if( nullptr != currentRefInfo ) { aiNode *node( currentRefInfo->m_node ); if( RefInfo::MeshRef == currentRefInfo->m_type ) { - for( size_t i = 0; i < currentRefInfo->m_Names.size(); i++ ) { + for( size_t i = 0; i < currentRefInfo->m_Names.size(); ++i ) { const std::string &name( currentRefInfo->m_Names[ i ] ); ReferenceMap::const_iterator it( m_mesh2refMap.find( name ) ); if( m_mesh2refMap.end() != it ) { @@ -1175,7 +1178,14 @@ void OpenGEXImporter::resolveReferences() { } } } else if( RefInfo::MaterialRef == currentRefInfo->m_type ) { - // ToDo! + for ( size_t i = 0; i < currentRefInfo->m_Names.size(); ++i ) { + const std::string name( currentRefInfo->m_Names[ i ] ); + ReferenceMap::const_iterator it( m_material2refMap.find( name ) ); + if ( m_material2refMap.end() != it ) { + unsigned int matIdx = static_cast< unsigned int >( m_material2refMap[ name ] ); + m_currentMesh->mMaterialIndex = matIdx; + } + } } else { throw DeadlyImportError( "Unknown reference info to resolve." ); } diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index e3a0735f6..bb6b45140 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -185,6 +185,7 @@ private: std::vector m_meshCache; typedef std::map ReferenceMap; std::map m_mesh2refMap; + std::map m_material2refMap; ODDLParser::Context *m_ctx; MetricInfo m_metrics[ MetricInfo::Max ]; From 6ec25be0a6a21595e878701c776f0ea8c0d3ea8a Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 2 Oct 2017 09:41:01 +0200 Subject: [PATCH 133/490] OpenGEX: improve logging to be able to detect error-prone situations. --- code/OpenGEXImporter.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 7c4d0f982..d9c58e91c 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -1182,8 +1182,16 @@ void OpenGEXImporter::resolveReferences() { const std::string name( currentRefInfo->m_Names[ i ] ); ReferenceMap::const_iterator it( m_material2refMap.find( name ) ); if ( m_material2refMap.end() != it ) { - unsigned int matIdx = static_cast< unsigned int >( m_material2refMap[ name ] ); - m_currentMesh->mMaterialIndex = matIdx; + if ( nullptr != m_currentMesh ) { + unsigned int matIdx = static_cast< unsigned int >( m_material2refMap[ name ] ); + if ( m_currentMesh->mMaterialIndex != 0 ) { + DefaultLogger::get()->warn( "Override of material reference in current mesh by material reference." ); + } + m_currentMesh->mMaterialIndex = matIdx; + } else { + DefaultLogger::get()->warn( "Cannot resolve material reference, because no current mesh is there." ); + + } } } } else { From 4652b66bb579bf09c921e498f2dd4c21f8669b62 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 12 Sep 2017 18:57:44 +0300 Subject: [PATCH 134/490] Add AddressSanitizer option to CMake --- CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index bd5c86140..a4248eb1e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,10 @@ OPTION ( ASSIMP_WERROR "Treat warnings as errors." OFF ) +OPTION ( ASSIMP_ASAN + "Enable AddressSanitizer." + OFF +) OPTION ( SYSTEM_IRRXML "Use system installed Irrlicht/IrrXML library." OFF @@ -223,6 +227,11 @@ if (ASSIMP_WERROR) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") endif() +if (ASSIMP_ASAN) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") +endif() + INCLUDE (FindPkgMacros) INCLUDE (PrecompiledHeader) From fff800f9ab8606c0f43cd92de1b291fec1796b1c Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 12 Sep 2017 19:09:22 +0300 Subject: [PATCH 135/490] Enable AddressSanitizer for Linux clang build --- .travis.sh | 2 +- .travis.yml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.sh b/.travis.sh index 2b09da35e..f19c3a000 100755 --- a/.travis.sh +++ b/.travis.sh @@ -1,6 +1,6 @@ function generate() { - cmake -G "Unix Makefiles" -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -DBUILD_SHARED_LIBS=$SHARED_BUILD -DASSIMP_COVERALLS=$ENABLE_COVERALLS -DASSIMP_ERROR=ON + cmake -G "Unix Makefiles" -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -DBUILD_SHARED_LIBS=$SHARED_BUILD -DASSIMP_COVERALLS=$ENABLE_COVERALLS -DASSIMP_ERROR=ON -DASSIMP_ASAN=$ASAN } if [ $ANDROID ]; then diff --git a/.travis.yml b/.travis.yml index cf0988517..d59689f78 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,16 +39,16 @@ matrix: include: - os: linux compiler: gcc - env: LINUX=1 TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON + env: LINUX=1 TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON ASAN=OFF - os: linux compiler: gcc - env: LINUX=1 TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF + env: LINUX=1 TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=ON - os: linux compiler: gcc - env: LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF + env: LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF - os: linux compiler: gcc - env: LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF + env: LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF install: - if [ $ANDROID ]; then wget -c http://dl.google.com/android/ndk/android-ndk-${PV}-${PLATF}.tar.bz2 && tar xf android-ndk-${PV}-${PLATF}.tar.bz2 ; fi From 5ecab20bd00692afff327875d360c94aec6b6016 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 12 Sep 2017 19:00:44 +0300 Subject: [PATCH 136/490] Fix delete / delete[] mismatch in glTFAsset --- code/glTFAsset.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/glTFAsset.inl b/code/glTFAsset.inl index 9284ccb02..61afebfb4 100644 --- a/code/glTFAsset.inl +++ b/code/glTFAsset.inl @@ -341,7 +341,7 @@ inline bool Buffer::LoadFromStream(IOStream& stream, size_t length, size_t baseO stream.Seek(baseOffset, aiOrigin_SET); } - mData.reset(new uint8_t[byteLength]); + mData.reset(new uint8_t[byteLength], std::default_delete()); if (stream.Read(mData.get(), byteLength, 1) != 1) { return false; From da96b32fb97e76f593e88e6093992db6500f2f7d Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 14 Sep 2017 09:38:07 +0300 Subject: [PATCH 137/490] Fix out-of-bounds read in MaterialSystem unit test --- test/unit/utMaterialSystem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/utMaterialSystem.cpp b/test/unit/utMaterialSystem.cpp index f6b534f4f..80408db00 100644 --- a/test/unit/utMaterialSystem.cpp +++ b/test/unit/utMaterialSystem.cpp @@ -73,7 +73,7 @@ TEST_F(MaterialSystemTest, testFloatArrayProperty) { float pf[] = {0.0f,1.0f,2.0f,3.0f}; unsigned int pMax = sizeof(pf) / sizeof(float); - this->pcMat->AddProperty(&pf,pMax,"testKey2"); + this->pcMat->AddProperty(pf,pMax,"testKey2"); pf[0] = pf[1] = pf[2] = pf[3] = 12.0f; EXPECT_EQ(AI_SUCCESS, pcMat->Get("testKey2",0,0,pf,&pMax)); @@ -97,7 +97,7 @@ TEST_F(MaterialSystemTest, testIntArrayProperty) { int pf[] = {0,1,2,3}; unsigned int pMax = sizeof(pf) / sizeof(int); - this->pcMat->AddProperty(&pf,pMax,"testKey4"); + this->pcMat->AddProperty(pf,pMax,"testKey4"); pf[0] = pf[1] = pf[2] = pf[3] = 12; EXPECT_EQ(AI_SUCCESS, pcMat->Get("testKey4",0,0,pf,&pMax)); From efd861253d9fc89f64335abbeb08786201156428 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 14 Sep 2017 10:33:49 +0300 Subject: [PATCH 138/490] Fix delete / delete[] mismatches in MakeVerboseFormat --- code/MakeVerboseFormat.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/MakeVerboseFormat.cpp b/code/MakeVerboseFormat.cpp index ee82caafe..720d44519 100644 --- a/code/MakeVerboseFormat.cpp +++ b/code/MakeVerboseFormat.cpp @@ -193,14 +193,14 @@ bool MakeVerboseFormatProcess::MakeVerboseFormat(aiMesh* pcMesh) p = 0; while (pcMesh->HasTextureCoords(p)) { - delete pcMesh->mTextureCoords[p]; + delete[] pcMesh->mTextureCoords[p]; pcMesh->mTextureCoords[p] = apvTextureCoords[p]; ++p; } p = 0; while (pcMesh->HasVertexColors(p)) { - delete pcMesh->mColors[p]; + delete[] pcMesh->mColors[p]; pcMesh->mColors[p] = apvColorSets[p]; ++p; } From 1095ec454b9e81d8336fa41ab7109f2226a754f0 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 14 Sep 2017 10:34:16 +0300 Subject: [PATCH 139/490] Fix delete / delete[] mismatches in glTF2 importer --- code/glTF2Asset.inl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 264a32d4d..ac0f353dc 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -357,7 +357,7 @@ inline bool Buffer::LoadFromStream(IOStream& stream, size_t length, size_t baseO stream.Seek(baseOffset, aiOrigin_SET); } - mData.reset(new uint8_t[byteLength]); + mData.reset(new uint8_t[byteLength], std::default_delete()); if (stream.Read(mData.get(), byteLength, 1) != 1) { return false; @@ -451,7 +451,7 @@ inline void Buffer::Grow(size_t amount) if (amount <= 0) return; uint8_t* b = new uint8_t[byteLength + amount]; if (mData) memcpy(b, mData.get(), byteLength); - mData.reset(b); + mData.reset(b, std::default_delete()); byteLength += amount; } From 29e46e4bb819f3499aa363c8df433b8c1529aa11 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 1 Oct 2017 19:25:43 +1100 Subject: [PATCH 140/490] Addressed asan failures caused by misuse of APIs within unit tests. --- test/unit/utObjTools.cpp | 3 ++- test/unit/utSharedPPData.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/unit/utObjTools.cpp b/test/unit/utObjTools.cpp index b359cfa3a..2d9e2779f 100644 --- a/test/unit/utObjTools.cpp +++ b/test/unit/utObjTools.cpp @@ -106,8 +106,9 @@ TEST_F( utObjTools, countComponents_TwoLines_Success ) { TestObjFileParser test_parser; std::string data( "-2.061493116917992e-15 -0.9009688496589661 \\\n-0.4338837265968323" ); std::vector buffer; - buffer.resize( data.size() ); + buffer.resize( data.size() + 1 ); ::memcpy( &buffer[ 0 ], &data[ 0 ], data.size() ); + buffer[ buffer.size() - 1 ] = '\0'; test_parser.setBuffer( buffer ); size_t numComps = test_parser.testGetNumComponentsInDataDefinition(); diff --git a/test/unit/utSharedPPData.cpp b/test/unit/utSharedPPData.cpp index e075365b8..495faa7ac 100644 --- a/test/unit/utSharedPPData.cpp +++ b/test/unit/utSharedPPData.cpp @@ -92,7 +92,7 @@ TEST_F(SharedPPDataTest, testPODProperty) // ------------------------------------------------------------------------------------------------ TEST_F(SharedPPDataTest, testPropertyPointer) { - int *i = new int[35]; + int *i = new int; shared->AddProperty("test16",i); int* o; EXPECT_TRUE(shared->GetProperty("test16",o)); From 1eb7eceddfc0b7f72f2055f4c400bbd24ea7b46f Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 1 Oct 2017 23:16:21 +1100 Subject: [PATCH 141/490] Addressed a number of memory leaks identified in unit tests by asan --- code/B3DImporter.cpp | 20 ++++++++++++++++++++ code/B3DImporter.h | 2 ++ code/PretransformVertices.cpp | 2 +- test/unit/TestModelFactory.h | 8 ++++---- test/unit/utIssues.cpp | 1 + test/unit/utObjImportExport.cpp | 9 +++++++++ test/unit/utRemoveVCProcess.cpp | 3 ++- test/unit/utSceneCombiner.cpp | 7 +++++-- 8 files changed, 44 insertions(+), 8 deletions(-) diff --git a/code/B3DImporter.cpp b/code/B3DImporter.cpp index 5de7bd67c..d15676128 100644 --- a/code/B3DImporter.cpp +++ b/code/B3DImporter.cpp @@ -82,6 +82,20 @@ static const aiImporterDesc desc = { //#define DEBUG_B3D +template +void DeleteAllBarePointers(std::vector& x) +{ + for(auto p : x) + { + delete p; + } +} + +B3DImporter::~B3DImporter() +{ + DeleteAllBarePointers(_animations); +} + // ------------------------------------------------------------------------------------------------ bool B3DImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const{ @@ -558,13 +572,19 @@ aiNode *B3DImporter::ReadNODE( aiNode *parent ){ void B3DImporter::ReadBB3D( aiScene *scene ){ _textures.clear(); + _materials.clear(); _vertices.clear(); + _meshes.clear(); + DeleteAllBarePointers(_nodes); _nodes.clear(); + _nodeAnims.clear(); + + DeleteAllBarePointers(_animations); _animations.clear(); string t=ReadChunk(); diff --git a/code/B3DImporter.h b/code/B3DImporter.h index 4d3576dc3..94644edd4 100644 --- a/code/B3DImporter.h +++ b/code/B3DImporter.h @@ -59,6 +59,8 @@ namespace Assimp{ class B3DImporter : public BaseImporter{ public: + B3DImporter() = default; + virtual ~B3DImporter(); virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; diff --git a/code/PretransformVertices.cpp b/code/PretransformVertices.cpp index 7bfed4292..5fc294618 100644 --- a/code/PretransformVertices.cpp +++ b/code/PretransformVertices.cpp @@ -634,7 +634,7 @@ void PretransformVertices::Execute( aiScene* pScene) aiNode* newRoot = new aiNode(); newRoot->mName = pScene->mRootNode->mName; delete pScene->mRootNode; - pScene->mRootNode = new aiNode(); + pScene->mRootNode = newRoot; if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras) { diff --git a/test/unit/TestModelFactory.h b/test/unit/TestModelFactory.h index 6c47dcf91..f848f5536 100644 --- a/test/unit/TestModelFactory.h +++ b/test/unit/TestModelFactory.h @@ -60,7 +60,7 @@ public: static aiScene *createDefaultTestModel( float &opacity ) { aiScene *scene( new aiScene ); scene->mNumMaterials = 1; - scene->mMaterials = new aiMaterial*; + scene->mMaterials = new aiMaterial*[scene->mNumMaterials]; scene->mMaterials[ 0 ] = new aiMaterial; aiColor3D color( 1, 0, 0 ); EXPECT_EQ( AI_SUCCESS, scene->mMaterials[ 0 ]->AddProperty( &color, 1, AI_MATKEY_COLOR_DIFFUSE ) ); @@ -70,7 +70,7 @@ public: EXPECT_EQ( AI_SUCCESS, scene->mMaterials[ 0 ]->AddProperty( &opacity, 1, AI_MATKEY_OPACITY ) ); scene->mNumMeshes = 1; - scene->mMeshes = new aiMesh*; + scene->mMeshes = new aiMesh*[scene->mNumMeshes]; scene->mMeshes[ 0 ] = new aiMesh; scene->mMeshes[ 0 ]->mMaterialIndex = 0; scene->mMeshes[ 0 ]->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; @@ -80,7 +80,7 @@ public: scene->mMeshes[ 0 ]->mVertices[ 1 ] = aiVector3D( 0, 1, 0 ); scene->mMeshes[ 0 ]->mVertices[ 2 ] = aiVector3D( 0, 0, 1 ); scene->mMeshes[ 0 ]->mNumFaces = 1; - scene->mMeshes[ 0 ]->mFaces = new aiFace; + scene->mMeshes[ 0 ]->mFaces = new aiFace[scene->mMeshes[ 0 ]->mNumFaces]; scene->mMeshes[ 0 ]->mFaces[ 0 ].mNumIndices = 3; scene->mMeshes[ 0 ]->mFaces[ 0 ].mIndices = new unsigned int[ 3 ]; scene->mMeshes[ 0 ]->mFaces[ 0 ].mIndices[ 0 ] = 0; @@ -89,7 +89,7 @@ public: scene->mRootNode = new aiNode; scene->mRootNode->mNumMeshes = 1; - scene->mRootNode->mMeshes = new unsigned int( 0 ); + scene->mRootNode->mMeshes = new unsigned int[scene->mRootNode->mNumMeshes]{ 0 }; return scene; } diff --git a/test/unit/utIssues.cpp b/test/unit/utIssues.cpp index a3e68fe1a..2feef922b 100644 --- a/test/unit/utIssues.cpp +++ b/test/unit/utIssues.cpp @@ -74,6 +74,7 @@ TEST_F( utIssues, OpacityBugWhenExporting_727 ) { EXPECT_EQ( AI_SUCCESS, newScene->mMaterials[ 0 ]->Get( AI_MATKEY_OPACITY, newOpacity ) ); EXPECT_EQ( opacity, newOpacity ); } + delete scene; } #endif // ASSIMP_BUILD_NO_EXPORT diff --git a/test/unit/utObjImportExport.cpp b/test/unit/utObjImportExport.cpp index d4d4fbf9e..7099252bb 100644 --- a/test/unit/utObjImportExport.cpp +++ b/test/unit/utObjImportExport.cpp @@ -237,6 +237,15 @@ TEST_F( utObjImportExport, obj_import_test ) { differ.showReport(); m_im->FreeScene(); + for(unsigned int i = 0; i < expected->mNumMeshes; ++i) + { + delete expected->mMeshes[i]; + } + delete[] expected->mMeshes; + expected->mMeshes = nullptr; + delete[] expected->mMaterials; + expected->mMaterials = nullptr; + delete expected; } TEST_F( utObjImportExport, issue1111_no_mat_name_Test ) { diff --git a/test/unit/utRemoveVCProcess.cpp b/test/unit/utRemoveVCProcess.cpp index a42c3e858..c78e80d3f 100644 --- a/test/unit/utRemoveVCProcess.cpp +++ b/test/unit/utRemoveVCProcess.cpp @@ -72,4 +72,5 @@ TEST_F( utRevmoveVCProcess, issue1266_ProcessMeshTest_NoCrash ) { scene->mMeshes[ 0 ] = mesh; RemoveVCProcess *process = new RemoveVCProcess; process->Execute( scene ); -} \ No newline at end of file + delete scene; +} diff --git a/test/unit/utSceneCombiner.cpp b/test/unit/utSceneCombiner.cpp index f0e4d5d90..3a283515e 100644 --- a/test/unit/utSceneCombiner.cpp +++ b/test/unit/utSceneCombiner.cpp @@ -42,6 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "UnitTestPCH.h" #include #include +#include using namespace ::Assimp; @@ -63,8 +64,10 @@ TEST_F( utSceneCombiner, MergeMeshes_ValidNames_Test ) { mesh3->mName.Set( "mesh_3" ); merge_list.push_back( mesh3 ); - aiMesh *out( nullptr ); - SceneCombiner::MergeMeshes( &out, 0, merge_list.begin(), merge_list.end() ); + std::unique_ptr out; + aiMesh* ptr = nullptr; + SceneCombiner::MergeMeshes( &ptr, 0, merge_list.begin(), merge_list.end() ); + out.reset(ptr); std::string outName = out->mName.C_Str(); EXPECT_EQ( "mesh_1.mesh_2.mesh_3", outName ); } From 5804667dbb4798c7c20c3ef51e4e1767148f1c7c Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 1 Oct 2017 17:51:13 +1100 Subject: [PATCH 142/490] Addressed some mismatched news/deletes caused by the new glTF2 sources. --- code/glTF2Asset.h | 2 +- code/glTF2Asset.inl | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index e2b61c646..63282dc6e 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -511,7 +511,7 @@ namespace glTF2 /// \fn ~SEncodedRegion() /// Destructor. - ~SEncodedRegion() { delete [] DecodedData; } + ~SEncodedRegion() { delete[] DecodedData; } }; /******************* Variables *******************/ diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index ac0f353dc..3082ebfab 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -313,7 +313,7 @@ inline void Buffer::Read(Value& obj, Asset& r) if (dataURI.base64) { uint8_t* data = 0; this->byteLength = Util::DecodeBase64(dataURI.data, dataURI.dataLength, data); - this->mData.reset(data); + this->mData.reset(data, std::default_delete()); if (statedLength > 0 && this->byteLength != statedLength) { throw DeadlyImportError("GLTF: buffer \"" + id + "\", expected " + to_string(statedLength) + @@ -326,7 +326,7 @@ inline void Buffer::Read(Value& obj, Asset& r) " bytes, but found " + to_string(dataURI.dataLength)); } - this->mData.reset(new uint8_t[dataURI.dataLength]); + this->mData.reset(new uint8_t[dataURI.dataLength], std::default_delete()); memcpy( this->mData.get(), dataURI.data, dataURI.dataLength ); } } @@ -432,7 +432,7 @@ uint8_t* new_data; // Copy data which place after replacing part. memcpy(&new_data[pBufferData_Offset + pReplace_Count], &mData.get()[pBufferData_Offset + pBufferData_Count], pBufferData_Offset); // Apply new data - mData.reset(new_data); + mData.reset(new_data, std::default_delete()); byteLength = new_data_size; return true; From 799f0a3ac8e3d0161402e4ba3e112ea41da95c9c Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Mon, 2 Oct 2017 11:40:57 +0300 Subject: [PATCH 143/490] Fix warnings-as-errors flag on MSVC --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index bd5c86140..ba219a443 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -219,8 +219,12 @@ if (ASSIMP_COVERALLS) endif() if (ASSIMP_WERROR) + IF (MSVC) + add_compile_options(/WX) + ELSE() SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") + ENDIF() endif() INCLUDE (FindPkgMacros) From 003c728dafd69430b4f3c7bbffa669a8c2be4764 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Mon, 2 Oct 2017 11:43:29 +0300 Subject: [PATCH 144/490] appveyor: Treat warnings as errors --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 991cf5bc1..b8828710b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -29,8 +29,8 @@ install: build_script: - cd c:\projects\assimp -- if "%platform%" equ "x64" (cmake CMakeLists.txt -G "Visual Studio %Configuration% Win64") -- if "%platform%" equ "x86" (cmake CMakeLists.txt -G "Visual Studio %Configuration%") +- if "%platform%" equ "x64" (cmake CMakeLists.txt -DASSIMP_WERROR=ON -G "Visual Studio %Configuration% Win64") +- if "%platform%" equ "x86" (cmake CMakeLists.txt -DASSIMP_WERROR=ON -G "Visual Studio %Configuration%") - if "%platform%" equ "x64" (msbuild /m /p:Configuration=Release /p:Platform="x64" Assimp.sln) - if "%platform%" equ "x86" (msbuild /m /p:Configuration=Release /p:Platform="Win32" Assimp.sln) From b5db7d3649f06abd153a2f81fd4da4f4f70da3a2 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Mon, 2 Oct 2017 13:08:20 +0300 Subject: [PATCH 145/490] Disable warning 4351 on MSVC 2013 --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index ba219a443..771800205 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -203,6 +203,11 @@ IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT CMAKE_COMPILER_IS_MINGW) ELSEIF(MSVC) # enable multi-core compilation with MSVC add_compile_options(/MP) + + # disable "elements of array '' will be default initialized" warning on MSVC2013 + IF(MSVC12) + add_compile_options(/wd4351) + ENDIF() ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" ) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fvisibility=hidden -fPIC -Wall -Wno-long-long -std=c++11" ) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") From 3964f5cf43ca61a8184f597f5e7df7dc29b9eeb8 Mon Sep 17 00:00:00 2001 From: Doug Stephen Date: Tue, 3 Oct 2017 14:11:23 -0500 Subject: [PATCH 146/490] Update .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 664708a90..12cd76a87 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,5 @@ lib64/assimp-vc120-mtd.ilk lib64/assimp-vc120-mtd.exp lib64/assimp-vc120-mt.exp xcuserdata + +cmake-build-debug From bab6ca20856b1fab87680cb19103ccc3bc3527f7 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Wed, 4 Oct 2017 20:33:59 +1100 Subject: [PATCH 147/490] Upgraded zlib to 1.2.11.1 from the develop branch. --- contrib/zlib/.gitignore | 26 + contrib/zlib/CMakeLists.txt | 11 +- contrib/zlib/ChangeLog | 1518 +++++++++++++++++++++++ contrib/zlib/FAQ | 368 ++++++ contrib/zlib/INDEX | 68 + contrib/zlib/Makefile.in | 410 ++++++ contrib/zlib/README | 6 +- contrib/zlib/adler32.c | 21 +- contrib/zlib/compress.c | 42 +- contrib/zlib/configure | 921 ++++++++++++++ contrib/zlib/crc32.c | 47 +- contrib/zlib/deflate.c | 877 ++++++++----- contrib/zlib/deflate.h | 35 +- contrib/zlib/gzguts.h | 23 +- contrib/zlib/gzlib.c | 31 +- contrib/zlib/gzread.c | 160 ++- contrib/zlib/gzwrite.c | 343 +++-- contrib/zlib/infback.c | 4 +- contrib/zlib/inffast.c | 111 +- contrib/zlib/inflate.c | 125 +- contrib/zlib/inflate.h | 11 +- contrib/zlib/inftrees.c | 26 +- contrib/zlib/make_vms.com | 867 +++++++++++++ contrib/zlib/treebuild.xml | 116 ++ contrib/zlib/trees.c | 99 +- contrib/zlib/uncompr.c | 114 +- contrib/zlib/win32/Makefile.gcc | 2 +- contrib/zlib/win32/Makefile.msc | 2 +- contrib/zlib/win32/README-WIN32.txt | 6 +- contrib/zlib/win32/VisualC.txt | 2 +- contrib/zlib/win32/zlib.def | 8 + contrib/zlib/win32/zlib1.rc | 2 +- contrib/zlib/zconf.h.cmakein | 41 +- contrib/zlib/{zconf.in.h => zconf.h.in} | 326 ++++- contrib/zlib/zconf.h.included | 43 +- contrib/zlib/zlib.3 | 149 +++ contrib/zlib/zlib.3.pdf | Bin 0 -> 19320 bytes contrib/zlib/zlib.h | 445 ++++--- contrib/zlib/zlib.map | 94 ++ contrib/zlib/zlib.pc.in | 13 + contrib/zlib/zlib2ansi | 152 +++ contrib/zlib/zutil.c | 53 +- contrib/zlib/zutil.h | 60 +- 43 files changed, 6670 insertions(+), 1108 deletions(-) create mode 100644 contrib/zlib/.gitignore create mode 100644 contrib/zlib/ChangeLog create mode 100644 contrib/zlib/FAQ create mode 100644 contrib/zlib/INDEX create mode 100644 contrib/zlib/Makefile.in create mode 100644 contrib/zlib/configure create mode 100644 contrib/zlib/make_vms.com create mode 100644 contrib/zlib/treebuild.xml rename contrib/zlib/{zconf.in.h => zconf.h.in} (50%) create mode 100644 contrib/zlib/zlib.3 create mode 100644 contrib/zlib/zlib.3.pdf create mode 100644 contrib/zlib/zlib.map create mode 100644 contrib/zlib/zlib.pc.in create mode 100644 contrib/zlib/zlib2ansi diff --git a/contrib/zlib/.gitignore b/contrib/zlib/.gitignore new file mode 100644 index 000000000..b1c7422fe --- /dev/null +++ b/contrib/zlib/.gitignore @@ -0,0 +1,26 @@ +*.diff +*.patch +*.orig +*.rej + +*~ +*.a +*.lo +*.o +*.dylib + +*.gcda +*.gcno +*.gcov + +/example +/example64 +/examplesh +/libz.so* +/minigzip +/minigzip64 +/minigzipsh +/zlib.pc +/configure.log + +.DS_Store diff --git a/contrib/zlib/CMakeLists.txt b/contrib/zlib/CMakeLists.txt index 022d3dfba..5f1368adb 100644 --- a/contrib/zlib/CMakeLists.txt +++ b/contrib/zlib/CMakeLists.txt @@ -10,10 +10,10 @@ endif() project(zlib C) cmake_policy(POP) -set(VERSION "1.2.8") +set(VERSION "1.2.11.1") -option(ASM686 "Enable building i686 assembly implementation for zlib") -option(AMD64 "Enable building amd64 assembly implementation for zlib") +option(ASM686 "Enable building i686 assembly implementation") +option(AMD64 "Enable building amd64 assembly implementation") #set(INSTALL_BIN_DIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Installation directory for executables") #set(INSTALL_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE PATH "Installation directory for libraries") @@ -90,7 +90,6 @@ configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/zlib.pc.cmakein ${ZLIB_PC} @ONLY) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/zconf.h.cmakein ${CMAKE_CURRENT_BINARY_DIR}/zconf.h @ONLY) - include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}) @@ -191,7 +190,11 @@ if(MINGW) set(ZLIB_DLL_SRCS ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj) endif(MINGW) +add_library(zlib SHARED ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS}) add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS}) +set_target_properties(zlib PROPERTIES DEFINE_SYMBOL ZLIB_DLL) +set_target_properties(zlib PROPERTIES SOVERSION 1) + INSTALL( TARGETS zlibstatic LIBRARY DESTINATION ${ASSIMP_LIB_INSTALL_DIR} ARCHIVE DESTINATION ${ASSIMP_LIB_INSTALL_DIR} diff --git a/contrib/zlib/ChangeLog b/contrib/zlib/ChangeLog new file mode 100644 index 000000000..9c6d95b8d --- /dev/null +++ b/contrib/zlib/ChangeLog @@ -0,0 +1,1518 @@ + + ChangeLog file for zlib + +Changes in 1.2.11.1 (xx Jan 2017) +- + +Changes in 1.2.11 (15 Jan 2017) +- Fix deflate stored bug when pulling last block from window +- Permit immediate deflateParams changes before any deflate input + +Changes in 1.2.10 (2 Jan 2017) +- Avoid warnings on snprintf() return value +- Fix bug in deflate_stored() for zero-length input +- Fix bug in gzwrite.c that produced corrupt gzip files +- Remove files to be installed before copying them in Makefile.in +- Add warnings when compiling with assembler code + +Changes in 1.2.9 (31 Dec 2016) +- Fix contrib/minizip to permit unzipping with desktop API [Zouzou] +- Improve contrib/blast to return unused bytes +- Assure that gzoffset() is correct when appending +- Improve compress() and uncompress() to support large lengths +- Fix bug in test/example.c where error code not saved +- Remedy Coverity warning [Randers-Pehrson] +- Improve speed of gzprintf() in transparent mode +- Fix inflateInit2() bug when windowBits is 16 or 32 +- Change DEBUG macro to ZLIB_DEBUG +- Avoid uninitialized access by gzclose_w() +- Allow building zlib outside of the source directory +- Fix bug that accepted invalid zlib header when windowBits is zero +- Fix gzseek() problem on MinGW due to buggy _lseeki64 there +- Loop on write() calls in gzwrite.c in case of non-blocking I/O +- Add --warn (-w) option to ./configure for more compiler warnings +- Reject a window size of 256 bytes if not using the zlib wrapper +- Fix bug when level 0 used with Z_HUFFMAN or Z_RLE +- Add --debug (-d) option to ./configure to define ZLIB_DEBUG +- Fix bugs in creating a very large gzip header +- Add uncompress2() function, which returns the input size used +- Assure that deflateParams() will not switch functions mid-block +- Dramatically speed up deflation for level 0 (storing) +- Add gzfread(), duplicating the interface of fread() +- Add gzfwrite(), duplicating the interface of fwrite() +- Add deflateGetDictionary() function +- Use snprintf() for later versions of Microsoft C +- Fix *Init macros to use z_ prefix when requested +- Replace as400 with os400 for OS/400 support [Monnerat] +- Add crc32_z() and adler32_z() functions with size_t lengths +- Update Visual Studio project files [AraHaan] + +Changes in 1.2.8 (28 Apr 2013) +- Update contrib/minizip/iowin32.c for Windows RT [Vollant] +- Do not force Z_CONST for C++ +- Clean up contrib/vstudio [Roß] +- Correct spelling error in zlib.h +- Fix mixed line endings in contrib/vstudio + +Changes in 1.2.7.3 (13 Apr 2013) +- Fix version numbers and DLL names in contrib/vstudio/*/zlib.rc + +Changes in 1.2.7.2 (13 Apr 2013) +- Change check for a four-byte type back to hexadecimal +- Fix typo in win32/Makefile.msc +- Add casts in gzwrite.c for pointer differences + +Changes in 1.2.7.1 (24 Mar 2013) +- Replace use of unsafe string functions with snprintf if available +- Avoid including stddef.h on Windows for Z_SOLO compile [Niessink] +- Fix gzgetc undefine when Z_PREFIX set [Turk] +- Eliminate use of mktemp in Makefile (not always available) +- Fix bug in 'F' mode for gzopen() +- Add inflateGetDictionary() function +- Correct comment in deflate.h +- Use _snprintf for snprintf in Microsoft C +- On Darwin, only use /usr/bin/libtool if libtool is not Apple +- Delete "--version" file if created by "ar --version" [Richard G.] +- Fix configure check for veracity of compiler error return codes +- Fix CMake compilation of static lib for MSVC2010 x64 +- Remove unused variable in infback9.c +- Fix argument checks in gzlog_compress() and gzlog_write() +- Clean up the usage of z_const and respect const usage within zlib +- Clean up examples/gzlog.[ch] comparisons of different types +- Avoid shift equal to bits in type (caused endless loop) +- Fix uninitialized value bug in gzputc() introduced by const patches +- Fix memory allocation error in examples/zran.c [Nor] +- Fix bug where gzopen(), gzclose() would write an empty file +- Fix bug in gzclose() when gzwrite() runs out of memory +- Check for input buffer malloc failure in examples/gzappend.c +- Add note to contrib/blast to use binary mode in stdio +- Fix comparisons of differently signed integers in contrib/blast +- Check for invalid code length codes in contrib/puff +- Fix serious but very rare decompression bug in inftrees.c +- Update inflateBack() comments, since inflate() can be faster +- Use underscored I/O function names for WINAPI_FAMILY +- Add _tr_flush_bits to the external symbols prefixed by --zprefix +- Add contrib/vstudio/vc10 pre-build step for static only +- Quote --version-script argument in CMakeLists.txt +- Don't specify --version-script on Apple platforms in CMakeLists.txt +- Fix casting error in contrib/testzlib/testzlib.c +- Fix types in contrib/minizip to match result of get_crc_table() +- Simplify contrib/vstudio/vc10 with 'd' suffix +- Add TOP support to win32/Makefile.msc +- Suport i686 and amd64 assembler builds in CMakeLists.txt +- Fix typos in the use of _LARGEFILE64_SOURCE in zconf.h +- Add vc11 and vc12 build files to contrib/vstudio +- Add gzvprintf() as an undocumented function in zlib +- Fix configure for Sun shell +- Remove runtime check in configure for four-byte integer type +- Add casts and consts to ease user conversion to C++ +- Add man pages for minizip and miniunzip +- In Makefile uninstall, don't rm if preceding cd fails +- Do not return Z_BUF_ERROR if deflateParam() has nothing to write + +Changes in 1.2.7 (2 May 2012) +- Replace use of memmove() with a simple copy for portability +- Test for existence of strerror +- Restore gzgetc_ for backward compatibility with 1.2.6 +- Fix build with non-GNU make on Solaris +- Require gcc 4.0 or later on Mac OS X to use the hidden attribute +- Include unistd.h for Watcom C +- Use __WATCOMC__ instead of __WATCOM__ +- Do not use the visibility attribute if NO_VIZ defined +- Improve the detection of no hidden visibility attribute +- Avoid using __int64 for gcc or solo compilation +- Cast to char * in gzprintf to avoid warnings [Zinser] +- Fix make_vms.com for VAX [Zinser] +- Don't use library or built-in byte swaps +- Simplify test and use of gcc hidden attribute +- Fix bug in gzclose_w() when gzwrite() fails to allocate memory +- Add "x" (O_EXCL) and "e" (O_CLOEXEC) modes support to gzopen() +- Fix bug in test/minigzip.c for configure --solo +- Fix contrib/vstudio project link errors [Mohanathas] +- Add ability to choose the builder in make_vms.com [Schweda] +- Add DESTDIR support to mingw32 win32/Makefile.gcc +- Fix comments in win32/Makefile.gcc for proper usage +- Allow overriding the default install locations for cmake +- Generate and install the pkg-config file with cmake +- Build both a static and a shared version of zlib with cmake +- Include version symbols for cmake builds +- If using cmake with MSVC, add the source directory to the includes +- Remove unneeded EXTRA_CFLAGS from win32/Makefile.gcc [Truta] +- Move obsolete emx makefile to old [Truta] +- Allow the use of -Wundef when compiling or using zlib +- Avoid the use of the -u option with mktemp +- Improve inflate() documentation on the use of Z_FINISH +- Recognize clang as gcc +- Add gzopen_w() in Windows for wide character path names +- Rename zconf.h in CMakeLists.txt to move it out of the way +- Add source directory in CMakeLists.txt for building examples +- Look in build directory for zlib.pc in CMakeLists.txt +- Remove gzflags from zlibvc.def in vc9 and vc10 +- Fix contrib/minizip compilation in the MinGW environment +- Update ./configure for Solaris, support --64 [Mooney] +- Remove -R. from Solaris shared build (possible security issue) +- Avoid race condition for parallel make (-j) running example +- Fix type mismatch between get_crc_table() and crc_table +- Fix parsing of version with "-" in CMakeLists.txt [Snider, Ziegler] +- Fix the path to zlib.map in CMakeLists.txt +- Force the native libtool in Mac OS X to avoid GNU libtool [Beebe] +- Add instructions to win32/Makefile.gcc for shared install [Torri] + +Changes in 1.2.6.1 (12 Feb 2012) +- Avoid the use of the Objective-C reserved name "id" +- Include io.h in gzguts.h for Microsoft compilers +- Fix problem with ./configure --prefix and gzgetc macro +- Include gz_header definition when compiling zlib solo +- Put gzflags() functionality back in zutil.c +- Avoid library header include in crc32.c for Z_SOLO +- Use name in GCC_CLASSIC as C compiler for coverage testing, if set +- Minor cleanup in contrib/minizip/zip.c [Vollant] +- Update make_vms.com [Zinser] +- Remove unnecessary gzgetc_ function +- Use optimized byte swap operations for Microsoft and GNU [Snyder] +- Fix minor typo in zlib.h comments [Rzesniowiecki] + +Changes in 1.2.6 (29 Jan 2012) +- Update the Pascal interface in contrib/pascal +- Fix function numbers for gzgetc_ in zlibvc.def files +- Fix configure.ac for contrib/minizip [Schiffer] +- Fix large-entry detection in minizip on 64-bit systems [Schiffer] +- Have ./configure use the compiler return code for error indication +- Fix CMakeLists.txt for cross compilation [McClure] +- Fix contrib/minizip/zip.c for 64-bit architectures [Dalsnes] +- Fix compilation of contrib/minizip on FreeBSD [Marquez] +- Correct suggested usages in win32/Makefile.msc [Shachar, Horvath] +- Include io.h for Turbo C / Borland C on all platforms [Truta] +- Make version explicit in contrib/minizip/configure.ac [Bosmans] +- Avoid warning for no encryption in contrib/minizip/zip.c [Vollant] +- Minor cleanup up contrib/minizip/unzip.c [Vollant] +- Fix bug when compiling minizip with C++ [Vollant] +- Protect for long name and extra fields in contrib/minizip [Vollant] +- Avoid some warnings in contrib/minizip [Vollant] +- Add -I../.. -L../.. to CFLAGS for minizip and miniunzip +- Add missing libs to minizip linker command +- Add support for VPATH builds in contrib/minizip +- Add an --enable-demos option to contrib/minizip/configure +- Add the generation of configure.log by ./configure +- Exit when required parameters not provided to win32/Makefile.gcc +- Have gzputc return the character written instead of the argument +- Use the -m option on ldconfig for BSD systems [Tobias] +- Correct in zlib.map when deflateResetKeep was added + +Changes in 1.2.5.3 (15 Jan 2012) +- Restore gzgetc function for binary compatibility +- Do not use _lseeki64 under Borland C++ [Truta] +- Update win32/Makefile.msc to build test/*.c [Truta] +- Remove old/visualc6 given CMakefile and other alternatives +- Update AS400 build files and documentation [Monnerat] +- Update win32/Makefile.gcc to build test/*.c [Truta] +- Permit stronger flushes after Z_BLOCK flushes +- Avoid extraneous empty blocks when doing empty flushes +- Permit Z_NULL arguments to deflatePending +- Allow deflatePrime() to insert bits in the middle of a stream +- Remove second empty static block for Z_PARTIAL_FLUSH +- Write out all of the available bits when using Z_BLOCK +- Insert the first two strings in the hash table after a flush + +Changes in 1.2.5.2 (17 Dec 2011) +- fix ld error: unable to find version dependency 'ZLIB_1.2.5' +- use relative symlinks for shared libs +- Avoid searching past window for Z_RLE strategy +- Assure that high-water mark initialization is always applied in deflate +- Add assertions to fill_window() in deflate.c to match comments +- Update python link in README +- Correct spelling error in gzread.c +- Fix bug in gzgets() for a concatenated empty gzip stream +- Correct error in comment for gz_make() +- Change gzread() and related to ignore junk after gzip streams +- Allow gzread() and related to continue after gzclearerr() +- Allow gzrewind() and gzseek() after a premature end-of-file +- Simplify gzseek() now that raw after gzip is ignored +- Change gzgetc() to a macro for speed (~40% speedup in testing) +- Fix gzclose() to return the actual error last encountered +- Always add large file support for windows +- Include zconf.h for windows large file support +- Include zconf.h.cmakein for windows large file support +- Update zconf.h.cmakein on make distclean +- Merge vestigial vsnprintf determination from zutil.h to gzguts.h +- Clarify how gzopen() appends in zlib.h comments +- Correct documentation of gzdirect() since junk at end now ignored +- Add a transparent write mode to gzopen() when 'T' is in the mode +- Update python link in zlib man page +- Get inffixed.h and MAKEFIXED result to match +- Add a ./config --solo option to make zlib subset with no library use +- Add undocumented inflateResetKeep() function for CAB file decoding +- Add --cover option to ./configure for gcc coverage testing +- Add #define ZLIB_CONST option to use const in the z_stream interface +- Add comment to gzdopen() in zlib.h to use dup() when using fileno() +- Note behavior of uncompress() to provide as much data as it can +- Add files in contrib/minizip to aid in building libminizip +- Split off AR options in Makefile.in and configure +- Change ON macro to Z_ARG to avoid application conflicts +- Facilitate compilation with Borland C++ for pragmas and vsnprintf +- Include io.h for Turbo C / Borland C++ +- Move example.c and minigzip.c to test/ +- Simplify incomplete code table filling in inflate_table() +- Remove code from inflate.c and infback.c that is impossible to execute +- Test the inflate code with full coverage +- Allow deflateSetDictionary, inflateSetDictionary at any time (in raw) +- Add deflateResetKeep and fix inflateResetKeep to retain dictionary +- Fix gzwrite.c to accommodate reduced memory zlib compilation +- Have inflate() with Z_FINISH avoid the allocation of a window +- Do not set strm->adler when doing raw inflate +- Fix gzeof() to behave just like feof() when read is not past end of file +- Fix bug in gzread.c when end-of-file is reached +- Avoid use of Z_BUF_ERROR in gz* functions except for premature EOF +- Document gzread() capability to read concurrently written files +- Remove hard-coding of resource compiler in CMakeLists.txt [Blammo] + +Changes in 1.2.5.1 (10 Sep 2011) +- Update FAQ entry on shared builds (#13) +- Avoid symbolic argument to chmod in Makefile.in +- Fix bug and add consts in contrib/puff [Oberhumer] +- Update contrib/puff/zeros.raw test file to have all block types +- Add full coverage test for puff in contrib/puff/Makefile +- Fix static-only-build install in Makefile.in +- Fix bug in unzGetCurrentFileInfo() in contrib/minizip [Kuno] +- Add libz.a dependency to shared in Makefile.in for parallel builds +- Spell out "number" (instead of "nb") in zlib.h for total_in, total_out +- Replace $(...) with `...` in configure for non-bash sh [Bowler] +- Add darwin* to Darwin* and solaris* to SunOS\ 5* in configure [Groffen] +- Add solaris* to Linux* in configure to allow gcc use [Groffen] +- Add *bsd* to Linux* case in configure [Bar-Lev] +- Add inffast.obj to dependencies in win32/Makefile.msc +- Correct spelling error in deflate.h [Kohler] +- Change libzdll.a again to libz.dll.a (!) in win32/Makefile.gcc +- Add test to configure for GNU C looking for gcc in output of $cc -v +- Add zlib.pc generation to win32/Makefile.gcc [Weigelt] +- Fix bug in zlib.h for _FILE_OFFSET_BITS set and _LARGEFILE64_SOURCE not +- Add comment in zlib.h that adler32_combine with len2 < 0 makes no sense +- Make NO_DIVIDE option in adler32.c much faster (thanks to John Reiser) +- Make stronger test in zconf.h to include unistd.h for LFS +- Apply Darwin patches for 64-bit file offsets to contrib/minizip [Slack] +- Fix zlib.h LFS support when Z_PREFIX used +- Add updated as400 support (removed from old) [Monnerat] +- Avoid deflate sensitivity to volatile input data +- Avoid division in adler32_combine for NO_DIVIDE +- Clarify the use of Z_FINISH with deflateBound() amount of space +- Set binary for output file in puff.c +- Use u4 type for crc_table to avoid conversion warnings +- Apply casts in zlib.h to avoid conversion warnings +- Add OF to prototypes for adler32_combine_ and crc32_combine_ [Miller] +- Improve inflateSync() documentation to note indeterminancy +- Add deflatePending() function to return the amount of pending output +- Correct the spelling of "specification" in FAQ [Randers-Pehrson] +- Add a check in configure for stdarg.h, use for gzprintf() +- Check that pointers fit in ints when gzprint() compiled old style +- Add dummy name before $(SHAREDLIBV) in Makefile [Bar-Lev, Bowler] +- Delete line in configure that adds -L. libz.a to LDFLAGS [Weigelt] +- Add debug records in assmebler code [Londer] +- Update RFC references to use http://tools.ietf.org/html/... [Li] +- Add --archs option, use of libtool to configure for Mac OS X [Borstel] + +Changes in 1.2.5 (19 Apr 2010) +- Disable visibility attribute in win32/Makefile.gcc [Bar-Lev] +- Default to libdir as sharedlibdir in configure [Nieder] +- Update copyright dates on modified source files +- Update trees.c to be able to generate modified trees.h +- Exit configure for MinGW, suggesting win32/Makefile.gcc +- Check for NULL path in gz_open [Homurlu] + +Changes in 1.2.4.5 (18 Apr 2010) +- Set sharedlibdir in configure [Torok] +- Set LDFLAGS in Makefile.in [Bar-Lev] +- Avoid mkdir objs race condition in Makefile.in [Bowler] +- Add ZLIB_INTERNAL in front of internal inter-module functions and arrays +- Define ZLIB_INTERNAL to hide internal functions and arrays for GNU C +- Don't use hidden attribute when it is a warning generator (e.g. Solaris) + +Changes in 1.2.4.4 (18 Apr 2010) +- Fix CROSS_PREFIX executable testing, CHOST extract, mingw* [Torok] +- Undefine _LARGEFILE64_SOURCE in zconf.h if it is zero, but not if empty +- Try to use bash or ksh regardless of functionality of /bin/sh +- Fix configure incompatibility with NetBSD sh +- Remove attempt to run under bash or ksh since have better NetBSD fix +- Fix win32/Makefile.gcc for MinGW [Bar-Lev] +- Add diagnostic messages when using CROSS_PREFIX in configure +- Added --sharedlibdir option to configure [Weigelt] +- Use hidden visibility attribute when available [Frysinger] + +Changes in 1.2.4.3 (10 Apr 2010) +- Only use CROSS_PREFIX in configure for ar and ranlib if they exist +- Use CROSS_PREFIX for nm [Bar-Lev] +- Assume _LARGEFILE64_SOURCE defined is equivalent to true +- Avoid use of undefined symbols in #if with && and || +- Make *64 prototypes in gzguts.h consistent with functions +- Add -shared load option for MinGW in configure [Bowler] +- Move z_off64_t to public interface, use instead of off64_t +- Remove ! from shell test in configure (not portable to Solaris) +- Change +0 macro tests to -0 for possibly increased portability + +Changes in 1.2.4.2 (9 Apr 2010) +- Add consistent carriage returns to readme.txt's in masmx86 and masmx64 +- Really provide prototypes for *64 functions when building without LFS +- Only define unlink() in minigzip.c if unistd.h not included +- Update README to point to contrib/vstudio project files +- Move projects/vc6 to old/ and remove projects/ +- Include stdlib.h in minigzip.c for setmode() definition under WinCE +- Clean up assembler builds in win32/Makefile.msc [Rowe] +- Include sys/types.h for Microsoft for off_t definition +- Fix memory leak on error in gz_open() +- Symbolize nm as $NM in configure [Weigelt] +- Use TEST_LDSHARED instead of LDSHARED to link test programs [Weigelt] +- Add +0 to _FILE_OFFSET_BITS and _LFS64_LARGEFILE in case not defined +- Fix bug in gzeof() to take into account unused input data +- Avoid initialization of structures with variables in puff.c +- Updated win32/README-WIN32.txt [Rowe] + +Changes in 1.2.4.1 (28 Mar 2010) +- Remove the use of [a-z] constructs for sed in configure [gentoo 310225] +- Remove $(SHAREDLIB) from LIBS in Makefile.in [Creech] +- Restore "for debugging" comment on sprintf() in gzlib.c +- Remove fdopen for MVS from gzguts.h +- Put new README-WIN32.txt in win32 [Rowe] +- Add check for shell to configure and invoke another shell if needed +- Fix big fat stinking bug in gzseek() on uncompressed files +- Remove vestigial F_OPEN64 define in zutil.h +- Set and check the value of _LARGEFILE_SOURCE and _LARGEFILE64_SOURCE +- Avoid errors on non-LFS systems when applications define LFS macros +- Set EXE to ".exe" in configure for MINGW [Kahle] +- Match crc32() in crc32.c exactly to the prototype in zlib.h [Sherrill] +- Add prefix for cross-compilation in win32/makefile.gcc [Bar-Lev] +- Add DLL install in win32/makefile.gcc [Bar-Lev] +- Allow Linux* or linux* from uname in configure [Bar-Lev] +- Allow ldconfig to be redefined in configure and Makefile.in [Bar-Lev] +- Add cross-compilation prefixes to configure [Bar-Lev] +- Match type exactly in gz_load() invocation in gzread.c +- Match type exactly of zcalloc() in zutil.c to zlib.h alloc_func +- Provide prototypes for *64 functions when building zlib without LFS +- Don't use -lc when linking shared library on MinGW +- Remove errno.h check in configure and vestigial errno code in zutil.h + +Changes in 1.2.4 (14 Mar 2010) +- Fix VER3 extraction in configure for no fourth subversion +- Update zlib.3, add docs to Makefile.in to make .pdf out of it +- Add zlib.3.pdf to distribution +- Don't set error code in gzerror() if passed pointer is NULL +- Apply destination directory fixes to CMakeLists.txt [Lowman] +- Move #cmakedefine's to a new zconf.in.cmakein +- Restore zconf.h for builds that don't use configure or cmake +- Add distclean to dummy Makefile for convenience +- Update and improve INDEX, README, and FAQ +- Update CMakeLists.txt for the return of zconf.h [Lowman] +- Update contrib/vstudio/vc9 and vc10 [Vollant] +- Change libz.dll.a back to libzdll.a in win32/Makefile.gcc +- Apply license and readme changes to contrib/asm686 [Raiter] +- Check file name lengths and add -c option in minigzip.c [Li] +- Update contrib/amd64 and contrib/masmx86/ [Vollant] +- Avoid use of "eof" parameter in trees.c to not shadow library variable +- Update make_vms.com for removal of zlibdefs.h [Zinser] +- Update assembler code and vstudio projects in contrib [Vollant] +- Remove outdated assembler code contrib/masm686 and contrib/asm586 +- Remove old vc7 and vc8 from contrib/vstudio +- Update win32/Makefile.msc, add ZLIB_VER_SUBREVISION [Rowe] +- Fix memory leaks in gzclose_r() and gzclose_w(), file leak in gz_open() +- Add contrib/gcc_gvmat64 for longest_match and inflate_fast [Vollant] +- Remove *64 functions from win32/zlib.def (they're not 64-bit yet) +- Fix bug in void-returning vsprintf() case in gzwrite.c +- Fix name change from inflate.h in contrib/inflate86/inffas86.c +- Check if temporary file exists before removing in make_vms.com [Zinser] +- Fix make install and uninstall for --static option +- Fix usage of _MSC_VER in gzguts.h and zutil.h [Truta] +- Update readme.txt in contrib/masmx64 and masmx86 to assemble + +Changes in 1.2.3.9 (21 Feb 2010) +- Expunge gzio.c +- Move as400 build information to old +- Fix updates in contrib/minizip and contrib/vstudio +- Add const to vsnprintf test in configure to avoid warnings [Weigelt] +- Delete zconf.h (made by configure) [Weigelt] +- Change zconf.in.h to zconf.h.in per convention [Weigelt] +- Check for NULL buf in gzgets() +- Return empty string for gzgets() with len == 1 (like fgets()) +- Fix description of gzgets() in zlib.h for end-of-file, NULL return +- Update minizip to 1.1 [Vollant] +- Avoid MSVC loss of data warnings in gzread.c, gzwrite.c +- Note in zlib.h that gzerror() should be used to distinguish from EOF +- Remove use of snprintf() from gzlib.c +- Fix bug in gzseek() +- Update contrib/vstudio, adding vc9 and vc10 [Kuno, Vollant] +- Fix zconf.h generation in CMakeLists.txt [Lowman] +- Improve comments in zconf.h where modified by configure + +Changes in 1.2.3.8 (13 Feb 2010) +- Clean up text files (tabs, trailing whitespace, etc.) [Oberhumer] +- Use z_off64_t in gz_zero() and gz_skip() to match state->skip +- Avoid comparison problem when sizeof(int) == sizeof(z_off64_t) +- Revert to Makefile.in from 1.2.3.6 (live with the clutter) +- Fix missing error return in gzflush(), add zlib.h note +- Add *64 functions to zlib.map [Levin] +- Fix signed/unsigned comparison in gz_comp() +- Use SFLAGS when testing shared linking in configure +- Add --64 option to ./configure to use -m64 with gcc +- Fix ./configure --help to correctly name options +- Have make fail if a test fails [Levin] +- Avoid buffer overrun in contrib/masmx64/gvmat64.asm [Simpson] +- Remove assembler object files from contrib + +Changes in 1.2.3.7 (24 Jan 2010) +- Always gzopen() with O_LARGEFILE if available +- Fix gzdirect() to work immediately after gzopen() or gzdopen() +- Make gzdirect() more precise when the state changes while reading +- Improve zlib.h documentation in many places +- Catch memory allocation failure in gz_open() +- Complete close operation if seek forward in gzclose_w() fails +- Return Z_ERRNO from gzclose_r() if close() fails +- Return Z_STREAM_ERROR instead of EOF for gzclose() being passed NULL +- Return zero for gzwrite() errors to match zlib.h description +- Return -1 on gzputs() error to match zlib.h description +- Add zconf.in.h to allow recovery from configure modification [Weigelt] +- Fix static library permissions in Makefile.in [Weigelt] +- Avoid warnings in configure tests that hide functionality [Weigelt] +- Add *BSD and DragonFly to Linux case in configure [gentoo 123571] +- Change libzdll.a to libz.dll.a in win32/Makefile.gcc [gentoo 288212] +- Avoid access of uninitialized data for first inflateReset2 call [Gomes] +- Keep object files in subdirectories to reduce the clutter somewhat +- Remove default Makefile and zlibdefs.h, add dummy Makefile +- Add new external functions to Z_PREFIX, remove duplicates, z_z_ -> z_ +- Remove zlibdefs.h completely -- modify zconf.h instead + +Changes in 1.2.3.6 (17 Jan 2010) +- Avoid void * arithmetic in gzread.c and gzwrite.c +- Make compilers happier with const char * for gz_error message +- Avoid unused parameter warning in inflate.c +- Avoid signed-unsigned comparison warning in inflate.c +- Indent #pragma's for traditional C +- Fix usage of strwinerror() in glib.c, change to gz_strwinerror() +- Correct email address in configure for system options +- Update make_vms.com and add make_vms.com to contrib/minizip [Zinser] +- Update zlib.map [Brown] +- Fix Makefile.in for Solaris 10 make of example64 and minizip64 [Torok] +- Apply various fixes to CMakeLists.txt [Lowman] +- Add checks on len in gzread() and gzwrite() +- Add error message for no more room for gzungetc() +- Remove zlib version check in gzwrite() +- Defer compression of gzprintf() result until need to +- Use snprintf() in gzdopen() if available +- Remove USE_MMAP configuration determination (only used by minigzip) +- Remove examples/pigz.c (available separately) +- Update examples/gun.c to 1.6 + +Changes in 1.2.3.5 (8 Jan 2010) +- Add space after #if in zutil.h for some compilers +- Fix relatively harmless bug in deflate_fast() [Exarevsky] +- Fix same problem in deflate_slow() +- Add $(SHAREDLIBV) to LIBS in Makefile.in [Brown] +- Add deflate_rle() for faster Z_RLE strategy run-length encoding +- Add deflate_huff() for faster Z_HUFFMAN_ONLY encoding +- Change name of "write" variable in inffast.c to avoid library collisions +- Fix premature EOF from gzread() in gzio.c [Brown] +- Use zlib header window size if windowBits is 0 in inflateInit2() +- Remove compressBound() call in deflate.c to avoid linking compress.o +- Replace use of errno in gz* with functions, support WinCE [Alves] +- Provide alternative to perror() in minigzip.c for WinCE [Alves] +- Don't use _vsnprintf on later versions of MSVC [Lowman] +- Add CMake build script and input file [Lowman] +- Update contrib/minizip to 1.1 [Svensson, Vollant] +- Moved nintendods directory from contrib to . +- Replace gzio.c with a new set of routines with the same functionality +- Add gzbuffer(), gzoffset(), gzclose_r(), gzclose_w() as part of above +- Update contrib/minizip to 1.1b +- Change gzeof() to return 0 on error instead of -1 to agree with zlib.h + +Changes in 1.2.3.4 (21 Dec 2009) +- Use old school .SUFFIXES in Makefile.in for FreeBSD compatibility +- Update comments in configure and Makefile.in for default --shared +- Fix test -z's in configure [Marquess] +- Build examplesh and minigzipsh when not testing +- Change NULL's to Z_NULL's in deflate.c and in comments in zlib.h +- Import LDFLAGS from the environment in configure +- Fix configure to populate SFLAGS with discovered CFLAGS options +- Adapt make_vms.com to the new Makefile.in [Zinser] +- Add zlib2ansi script for C++ compilation [Marquess] +- Add _FILE_OFFSET_BITS=64 test to make test (when applicable) +- Add AMD64 assembler code for longest match to contrib [Teterin] +- Include options from $SFLAGS when doing $LDSHARED +- Simplify 64-bit file support by introducing z_off64_t type +- Make shared object files in objs directory to work around old Sun cc +- Use only three-part version number for Darwin shared compiles +- Add rc option to ar in Makefile.in for when ./configure not run +- Add -WI,-rpath,. to LDFLAGS for OSF 1 V4* +- Set LD_LIBRARYN32_PATH for SGI IRIX shared compile +- Protect against _FILE_OFFSET_BITS being defined when compiling zlib +- Rename Makefile.in targets allstatic to static and allshared to shared +- Fix static and shared Makefile.in targets to be independent +- Correct error return bug in gz_open() by setting state [Brown] +- Put spaces before ;;'s in configure for better sh compatibility +- Add pigz.c (parallel implementation of gzip) to examples/ +- Correct constant in crc32.c to UL [Leventhal] +- Reject negative lengths in crc32_combine() +- Add inflateReset2() function to work like inflateEnd()/inflateInit2() +- Include sys/types.h for _LARGEFILE64_SOURCE [Brown] +- Correct typo in doc/algorithm.txt [Janik] +- Fix bug in adler32_combine() [Zhu] +- Catch missing-end-of-block-code error in all inflates and in puff + Assures that random input to inflate eventually results in an error +- Added enough.c (calculation of ENOUGH for inftrees.h) to examples/ +- Update ENOUGH and its usage to reflect discovered bounds +- Fix gzerror() error report on empty input file [Brown] +- Add ush casts in trees.c to avoid pedantic runtime errors +- Fix typo in zlib.h uncompress() description [Reiss] +- Correct inflate() comments with regard to automatic header detection +- Remove deprecation comment on Z_PARTIAL_FLUSH (it stays) +- Put new version of gzlog (2.0) in examples with interruption recovery +- Add puff compile option to permit invalid distance-too-far streams +- Add puff TEST command options, ability to read piped input +- Prototype the *64 functions in zlib.h when _FILE_OFFSET_BITS == 64, but + _LARGEFILE64_SOURCE not defined +- Fix Z_FULL_FLUSH to truly erase the past by resetting s->strstart +- Fix deflateSetDictionary() to use all 32K for output consistency +- Remove extraneous #define MIN_LOOKAHEAD in deflate.c (in deflate.h) +- Clear bytes after deflate lookahead to avoid use of uninitialized data +- Change a limit in inftrees.c to be more transparent to Coverity Prevent +- Update win32/zlib.def with exported symbols from zlib.h +- Correct spelling errors in zlib.h [Willem, Sobrado] +- Allow Z_BLOCK for deflate() to force a new block +- Allow negative bits in inflatePrime() to delete existing bit buffer +- Add Z_TREES flush option to inflate() to return at end of trees +- Add inflateMark() to return current state information for random access +- Add Makefile for NintendoDS to contrib [Costa] +- Add -w in configure compile tests to avoid spurious warnings [Beucler] +- Fix typos in zlib.h comments for deflateSetDictionary() +- Fix EOF detection in transparent gzread() [Maier] + +Changes in 1.2.3.3 (2 October 2006) +- Make --shared the default for configure, add a --static option +- Add compile option to permit invalid distance-too-far streams +- Add inflateUndermine() function which is required to enable above +- Remove use of "this" variable name for C++ compatibility [Marquess] +- Add testing of shared library in make test, if shared library built +- Use ftello() and fseeko() if available instead of ftell() and fseek() +- Provide two versions of all functions that use the z_off_t type for + binary compatibility -- a normal version and a 64-bit offset version, + per the Large File Support Extension when _LARGEFILE64_SOURCE is + defined; use the 64-bit versions by default when _FILE_OFFSET_BITS + is defined to be 64 +- Add a --uname= option to configure to perhaps help with cross-compiling + +Changes in 1.2.3.2 (3 September 2006) +- Turn off silly Borland warnings [Hay] +- Use off64_t and define _LARGEFILE64_SOURCE when present +- Fix missing dependency on inffixed.h in Makefile.in +- Rig configure --shared to build both shared and static [Teredesai, Truta] +- Remove zconf.in.h and instead create a new zlibdefs.h file +- Fix contrib/minizip/unzip.c non-encrypted after encrypted [Vollant] +- Add treebuild.xml (see http://treebuild.metux.de/) [Weigelt] + +Changes in 1.2.3.1 (16 August 2006) +- Add watcom directory with OpenWatcom make files [Daniel] +- Remove #undef of FAR in zconf.in.h for MVS [Fedtke] +- Update make_vms.com [Zinser] +- Use -fPIC for shared build in configure [Teredesai, Nicholson] +- Use only major version number for libz.so on IRIX and OSF1 [Reinholdtsen] +- Use fdopen() (not _fdopen()) for Interix in zutil.h [Bäck] +- Add some FAQ entries about the contrib directory +- Update the MVS question in the FAQ +- Avoid extraneous reads after EOF in gzio.c [Brown] +- Correct spelling of "successfully" in gzio.c [Randers-Pehrson] +- Add comments to zlib.h about gzerror() usage [Brown] +- Set extra flags in gzip header in gzopen() like deflate() does +- Make configure options more compatible with double-dash conventions + [Weigelt] +- Clean up compilation under Solaris SunStudio cc [Rowe, Reinholdtsen] +- Fix uninstall target in Makefile.in [Truta] +- Add pkgconfig support [Weigelt] +- Use $(DESTDIR) macro in Makefile.in [Reinholdtsen, Weigelt] +- Replace set_data_type() with a more accurate detect_data_type() in + trees.c, according to the txtvsbin.txt document [Truta] +- Swap the order of #include and #include "zlib.h" in + gzio.c, example.c and minigzip.c [Truta] +- Shut up annoying VS2005 warnings about standard C deprecation [Rowe, + Truta] (where?) +- Fix target "clean" from win32/Makefile.bor [Truta] +- Create .pdb and .manifest files in win32/makefile.msc [Ziegler, Rowe] +- Update zlib www home address in win32/DLL_FAQ.txt [Truta] +- Update contrib/masmx86/inffas32.asm for VS2005 [Vollant, Van Wassenhove] +- Enable browse info in the "Debug" and "ASM Debug" configurations in + the Visual C++ 6 project, and set (non-ASM) "Debug" as default [Truta] +- Add pkgconfig support [Weigelt] +- Add ZLIB_VER_MAJOR, ZLIB_VER_MINOR and ZLIB_VER_REVISION in zlib.h, + for use in win32/zlib1.rc [Polushin, Rowe, Truta] +- Add a document that explains the new text detection scheme to + doc/txtvsbin.txt [Truta] +- Add rfc1950.txt, rfc1951.txt and rfc1952.txt to doc/ [Truta] +- Move algorithm.txt into doc/ [Truta] +- Synchronize FAQ with website +- Fix compressBound(), was low for some pathological cases [Fearnley] +- Take into account wrapper variations in deflateBound() +- Set examples/zpipe.c input and output to binary mode for Windows +- Update examples/zlib_how.html with new zpipe.c (also web site) +- Fix some warnings in examples/gzlog.c and examples/zran.c (it seems + that gcc became pickier in 4.0) +- Add zlib.map for Linux: "All symbols from zlib-1.1.4 remain + un-versioned, the patch adds versioning only for symbols introduced in + zlib-1.2.0 or later. It also declares as local those symbols which are + not designed to be exported." [Levin] +- Update Z_PREFIX list in zconf.in.h, add --zprefix option to configure +- Do not initialize global static by default in trees.c, add a response + NO_INIT_GLOBAL_POINTERS to initialize them if needed [Marquess] +- Don't use strerror() in gzio.c under WinCE [Yakimov] +- Don't use errno.h in zutil.h under WinCE [Yakimov] +- Move arguments for AR to its usage to allow replacing ar [Marot] +- Add HAVE_VISIBILITY_PRAGMA in zconf.in.h for Mozilla [Randers-Pehrson] +- Improve inflateInit() and inflateInit2() documentation +- Fix structure size comment in inflate.h +- Change configure help option from --h* to --help [Santos] + +Changes in 1.2.3 (18 July 2005) +- Apply security vulnerability fixes to contrib/infback9 as well +- Clean up some text files (carriage returns, trailing space) +- Update testzlib, vstudio, masmx64, and masmx86 in contrib [Vollant] + +Changes in 1.2.2.4 (11 July 2005) +- Add inflatePrime() function for starting inflation at bit boundary +- Avoid some Visual C warnings in deflate.c +- Avoid more silly Visual C warnings in inflate.c and inftrees.c for 64-bit + compile +- Fix some spelling errors in comments [Betts] +- Correct inflateInit2() error return documentation in zlib.h +- Add zran.c example of compressed data random access to examples + directory, shows use of inflatePrime() +- Fix cast for assignments to strm->state in inflate.c and infback.c +- Fix zlibCompileFlags() in zutil.c to use 1L for long shifts [Oberhumer] +- Move declarations of gf2 functions to right place in crc32.c [Oberhumer] +- Add cast in trees.c t avoid a warning [Oberhumer] +- Avoid some warnings in fitblk.c, gun.c, gzjoin.c in examples [Oberhumer] +- Update make_vms.com [Zinser] +- Initialize state->write in inflateReset() since copied in inflate_fast() +- Be more strict on incomplete code sets in inflate_table() and increase + ENOUGH and MAXD -- this repairs a possible security vulnerability for + invalid inflate input. Thanks to Tavis Ormandy and Markus Oberhumer for + discovering the vulnerability and providing test cases. +- Add ia64 support to configure for HP-UX [Smith] +- Add error return to gzread() for format or i/o error [Levin] +- Use malloc.h for OS/2 [Necasek] + +Changes in 1.2.2.3 (27 May 2005) +- Replace 1U constants in inflate.c and inftrees.c for 64-bit compile +- Typecast fread() return values in gzio.c [Vollant] +- Remove trailing space in minigzip.c outmode (VC++ can't deal with it) +- Fix crc check bug in gzread() after gzungetc() [Heiner] +- Add the deflateTune() function to adjust internal compression parameters +- Add a fast gzip decompressor, gun.c, to examples (use of inflateBack) +- Remove an incorrect assertion in examples/zpipe.c +- Add C++ wrapper in infback9.h [Donais] +- Fix bug in inflateCopy() when decoding fixed codes +- Note in zlib.h how much deflateSetDictionary() actually uses +- Remove USE_DICT_HEAD in deflate.c (would mess up inflate if used) +- Add _WIN32_WCE to define WIN32 in zconf.in.h [Spencer] +- Don't include stderr.h or errno.h for _WIN32_WCE in zutil.h [Spencer] +- Add gzdirect() function to indicate transparent reads +- Update contrib/minizip [Vollant] +- Fix compilation of deflate.c when both ASMV and FASTEST [Oberhumer] +- Add casts in crc32.c to avoid warnings [Oberhumer] +- Add contrib/masmx64 [Vollant] +- Update contrib/asm586, asm686, masmx86, testzlib, vstudio [Vollant] + +Changes in 1.2.2.2 (30 December 2004) +- Replace structure assignments in deflate.c and inflate.c with zmemcpy to + avoid implicit memcpy calls (portability for no-library compilation) +- Increase sprintf() buffer size in gzdopen() to allow for large numbers +- Add INFLATE_STRICT to check distances against zlib header +- Improve WinCE errno handling and comments [Chang] +- Remove comment about no gzip header processing in FAQ +- Add Z_FIXED strategy option to deflateInit2() to force fixed trees +- Add updated make_vms.com [Coghlan], update README +- Create a new "examples" directory, move gzappend.c there, add zpipe.c, + fitblk.c, gzlog.[ch], gzjoin.c, and zlib_how.html. +- Add FAQ entry and comments in deflate.c on uninitialized memory access +- Add Solaris 9 make options in configure [Gilbert] +- Allow strerror() usage in gzio.c for STDC +- Fix DecompressBuf in contrib/delphi/ZLib.pas [ManChesTer] +- Update contrib/masmx86/inffas32.asm and gvmat32.asm [Vollant] +- Use z_off_t for adler32_combine() and crc32_combine() lengths +- Make adler32() much faster for small len +- Use OS_CODE in deflate() default gzip header + +Changes in 1.2.2.1 (31 October 2004) +- Allow inflateSetDictionary() call for raw inflate +- Fix inflate header crc check bug for file names and comments +- Add deflateSetHeader() and gz_header structure for custom gzip headers +- Add inflateGetheader() to retrieve gzip headers +- Add crc32_combine() and adler32_combine() functions +- Add alloc_func, free_func, in_func, out_func to Z_PREFIX list +- Use zstreamp consistently in zlib.h (inflate_back functions) +- Remove GUNZIP condition from definition of inflate_mode in inflate.h + and in contrib/inflate86/inffast.S [Truta, Anderson] +- Add support for AMD64 in contrib/inflate86/inffas86.c [Anderson] +- Update projects/README.projects and projects/visualc6 [Truta] +- Update win32/DLL_FAQ.txt [Truta] +- Avoid warning under NO_GZCOMPRESS in gzio.c; fix typo [Truta] +- Deprecate Z_ASCII; use Z_TEXT instead [Truta] +- Use a new algorithm for setting strm->data_type in trees.c [Truta] +- Do not define an exit() prototype in zutil.c unless DEBUG defined +- Remove prototype of exit() from zutil.c, example.c, minigzip.c [Truta] +- Add comment in zlib.h for Z_NO_FLUSH parameter to deflate() +- Fix Darwin build version identification [Peterson] + +Changes in 1.2.2 (3 October 2004) +- Update zlib.h comments on gzip in-memory processing +- Set adler to 1 in inflateReset() to support Java test suite [Walles] +- Add contrib/dotzlib [Ravn] +- Update win32/DLL_FAQ.txt [Truta] +- Update contrib/minizip [Vollant] +- Move contrib/visual-basic.txt to old/ [Truta] +- Fix assembler builds in projects/visualc6/ [Truta] + +Changes in 1.2.1.2 (9 September 2004) +- Update INDEX file +- Fix trees.c to update strm->data_type (no one ever noticed!) +- Fix bug in error case in inflate.c, infback.c, and infback9.c [Brown] +- Add "volatile" to crc table flag declaration (for DYNAMIC_CRC_TABLE) +- Add limited multitasking protection to DYNAMIC_CRC_TABLE +- Add NO_vsnprintf for VMS in zutil.h [Mozilla] +- Don't declare strerror() under VMS [Mozilla] +- Add comment to DYNAMIC_CRC_TABLE to use get_crc_table() to initialize +- Update contrib/ada [Anisimkov] +- Update contrib/minizip [Vollant] +- Fix configure to not hardcode directories for Darwin [Peterson] +- Fix gzio.c to not return error on empty files [Brown] +- Fix indentation; update version in contrib/delphi/ZLib.pas and + contrib/pascal/zlibpas.pas [Truta] +- Update mkasm.bat in contrib/masmx86 [Truta] +- Update contrib/untgz [Truta] +- Add projects/README.projects [Truta] +- Add project for MS Visual C++ 6.0 in projects/visualc6 [Cadieux, Truta] +- Update win32/DLL_FAQ.txt [Truta] +- Update list of Z_PREFIX symbols in zconf.h [Randers-Pehrson, Truta] +- Remove an unnecessary assignment to curr in inftrees.c [Truta] +- Add OS/2 to exe builds in configure [Poltorak] +- Remove err dummy parameter in zlib.h [Kientzle] + +Changes in 1.2.1.1 (9 January 2004) +- Update email address in README +- Several FAQ updates +- Fix a big fat bug in inftrees.c that prevented decoding valid + dynamic blocks with only literals and no distance codes -- + Thanks to "Hot Emu" for the bug report and sample file +- Add a note to puff.c on no distance codes case. + +Changes in 1.2.1 (17 November 2003) +- Remove a tab in contrib/gzappend/gzappend.c +- Update some interfaces in contrib for new zlib functions +- Update zlib version number in some contrib entries +- Add Windows CE definition for ptrdiff_t in zutil.h [Mai, Truta] +- Support shared libraries on Hurd and KFreeBSD [Brown] +- Fix error in NO_DIVIDE option of adler32.c + +Changes in 1.2.0.8 (4 November 2003) +- Update version in contrib/delphi/ZLib.pas and contrib/pascal/zlibpas.pas +- Add experimental NO_DIVIDE #define in adler32.c + - Possibly faster on some processors (let me know if it is) +- Correct Z_BLOCK to not return on first inflate call if no wrap +- Fix strm->data_type on inflate() return to correctly indicate EOB +- Add deflatePrime() function for appending in the middle of a byte +- Add contrib/gzappend for an example of appending to a stream +- Update win32/DLL_FAQ.txt [Truta] +- Delete Turbo C comment in README [Truta] +- Improve some indentation in zconf.h [Truta] +- Fix infinite loop on bad input in configure script [Church] +- Fix gzeof() for concatenated gzip files [Johnson] +- Add example to contrib/visual-basic.txt [Michael B.] +- Add -p to mkdir's in Makefile.in [vda] +- Fix configure to properly detect presence or lack of printf functions +- Add AS400 support [Monnerat] +- Add a little Cygwin support [Wilson] + +Changes in 1.2.0.7 (21 September 2003) +- Correct some debug formats in contrib/infback9 +- Cast a type in a debug statement in trees.c +- Change search and replace delimiter in configure from % to # [Beebe] +- Update contrib/untgz to 0.2 with various fixes [Truta] +- Add build support for Amiga [Nikl] +- Remove some directories in old that have been updated to 1.2 +- Add dylib building for Mac OS X in configure and Makefile.in +- Remove old distribution stuff from Makefile +- Update README to point to DLL_FAQ.txt, and add comment on Mac OS X +- Update links in README + +Changes in 1.2.0.6 (13 September 2003) +- Minor FAQ updates +- Update contrib/minizip to 1.00 [Vollant] +- Remove test of gz functions in example.c when GZ_COMPRESS defined [Truta] +- Update POSTINC comment for 68060 [Nikl] +- Add contrib/infback9 with deflate64 decoding (unsupported) +- For MVS define NO_vsnprintf and undefine FAR [van Burik] +- Add pragma for fdopen on MVS [van Burik] + +Changes in 1.2.0.5 (8 September 2003) +- Add OF to inflateBackEnd() declaration in zlib.h +- Remember start when using gzdopen in the middle of a file +- Use internal off_t counters in gz* functions to properly handle seeks +- Perform more rigorous check for distance-too-far in inffast.c +- Add Z_BLOCK flush option to return from inflate at block boundary +- Set strm->data_type on return from inflate + - Indicate bits unused, if at block boundary, and if in last block +- Replace size_t with ptrdiff_t in crc32.c, and check for correct size +- Add condition so old NO_DEFLATE define still works for compatibility +- FAQ update regarding the Windows DLL [Truta] +- INDEX update: add qnx entry, remove aix entry [Truta] +- Install zlib.3 into mandir [Wilson] +- Move contrib/zlib_dll_FAQ.txt to win32/DLL_FAQ.txt; update [Truta] +- Adapt the zlib interface to the new DLL convention guidelines [Truta] +- Introduce ZLIB_WINAPI macro to allow the export of functions using + the WINAPI calling convention, for Visual Basic [Vollant, Truta] +- Update msdos and win32 scripts and makefiles [Truta] +- Export symbols by name, not by ordinal, in win32/zlib.def [Truta] +- Add contrib/ada [Anisimkov] +- Move asm files from contrib/vstudio/vc70_32 to contrib/asm386 [Truta] +- Rename contrib/asm386 to contrib/masmx86 [Truta, Vollant] +- Add contrib/masm686 [Truta] +- Fix offsets in contrib/inflate86 and contrib/masmx86/inffas32.asm + [Truta, Vollant] +- Update contrib/delphi; rename to contrib/pascal; add example [Truta] +- Remove contrib/delphi2; add a new contrib/delphi [Truta] +- Avoid inclusion of the nonstandard in contrib/iostream, + and fix some method prototypes [Truta] +- Fix the ZCR_SEED2 constant to avoid warnings in contrib/minizip + [Truta] +- Avoid the use of backslash (\) in contrib/minizip [Vollant] +- Fix file time handling in contrib/untgz; update makefiles [Truta] +- Update contrib/vstudio/vc70_32 to comply with the new DLL guidelines + [Vollant] +- Remove contrib/vstudio/vc15_16 [Vollant] +- Rename contrib/vstudio/vc70_32 to contrib/vstudio/vc7 [Truta] +- Update README.contrib [Truta] +- Invert the assignment order of match_head and s->prev[...] in + INSERT_STRING [Truta] +- Compare TOO_FAR with 32767 instead of 32768, to avoid 16-bit warnings + [Truta] +- Compare function pointers with 0, not with NULL or Z_NULL [Truta] +- Fix prototype of syncsearch in inflate.c [Truta] +- Introduce ASMINF macro to be enabled when using an ASM implementation + of inflate_fast [Truta] +- Change NO_DEFLATE to NO_GZCOMPRESS [Truta] +- Modify test_gzio in example.c to take a single file name as a + parameter [Truta] +- Exit the example.c program if gzopen fails [Truta] +- Add type casts around strlen in example.c [Truta] +- Remove casting to sizeof in minigzip.c; give a proper type + to the variable compared with SUFFIX_LEN [Truta] +- Update definitions of STDC and STDC99 in zconf.h [Truta] +- Synchronize zconf.h with the new Windows DLL interface [Truta] +- Use SYS16BIT instead of __32BIT__ to distinguish between + 16- and 32-bit platforms [Truta] +- Use far memory allocators in small 16-bit memory models for + Turbo C [Truta] +- Add info about the use of ASMV, ASMINF and ZLIB_WINAPI in + zlibCompileFlags [Truta] +- Cygwin has vsnprintf [Wilson] +- In Windows16, OS_CODE is 0, as in MSDOS [Truta] +- In Cygwin, OS_CODE is 3 (Unix), not 11 (Windows32) [Wilson] + +Changes in 1.2.0.4 (10 August 2003) +- Minor FAQ updates +- Be more strict when checking inflateInit2's windowBits parameter +- Change NO_GUNZIP compile option to NO_GZIP to cover deflate as well +- Add gzip wrapper option to deflateInit2 using windowBits +- Add updated QNX rule in configure and qnx directory [Bonnefoy] +- Make inflate distance-too-far checks more rigorous +- Clean up FAR usage in inflate +- Add casting to sizeof() in gzio.c and minigzip.c + +Changes in 1.2.0.3 (19 July 2003) +- Fix silly error in gzungetc() implementation [Vollant] +- Update contrib/minizip and contrib/vstudio [Vollant] +- Fix printf format in example.c +- Correct cdecl support in zconf.in.h [Anisimkov] +- Minor FAQ updates + +Changes in 1.2.0.2 (13 July 2003) +- Add ZLIB_VERNUM in zlib.h for numerical preprocessor comparisons +- Attempt to avoid warnings in crc32.c for pointer-int conversion +- Add AIX to configure, remove aix directory [Bakker] +- Add some casts to minigzip.c +- Improve checking after insecure sprintf() or vsprintf() calls +- Remove #elif's from crc32.c +- Change leave label to inf_leave in inflate.c and infback.c to avoid + library conflicts +- Remove inflate gzip decoding by default--only enable gzip decoding by + special request for stricter backward compatibility +- Add zlibCompileFlags() function to return compilation information +- More typecasting in deflate.c to avoid warnings +- Remove leading underscore from _Capital #defines [Truta] +- Fix configure to link shared library when testing +- Add some Windows CE target adjustments [Mai] +- Remove #define ZLIB_DLL in zconf.h [Vollant] +- Add zlib.3 [Rodgers] +- Update RFC URL in deflate.c and algorithm.txt [Mai] +- Add zlib_dll_FAQ.txt to contrib [Truta] +- Add UL to some constants [Truta] +- Update minizip and vstudio [Vollant] +- Remove vestigial NEED_DUMMY_RETURN from zconf.in.h +- Expand use of NO_DUMMY_DECL to avoid all dummy structures +- Added iostream3 to contrib [Schwardt] +- Replace rewind() with fseek() for WinCE [Truta] +- Improve setting of zlib format compression level flags + - Report 0 for huffman and rle strategies and for level == 0 or 1 + - Report 2 only for level == 6 +- Only deal with 64K limit when necessary at compile time [Truta] +- Allow TOO_FAR check to be turned off at compile time [Truta] +- Add gzclearerr() function [Souza] +- Add gzungetc() function + +Changes in 1.2.0.1 (17 March 2003) +- Add Z_RLE strategy for run-length encoding [Truta] + - When Z_RLE requested, restrict matches to distance one + - Update zlib.h, minigzip.c, gzopen(), gzdopen() for Z_RLE +- Correct FASTEST compilation to allow level == 0 +- Clean up what gets compiled for FASTEST +- Incorporate changes to zconf.in.h [Vollant] + - Refine detection of Turbo C need for dummy returns + - Refine ZLIB_DLL compilation + - Include additional header file on VMS for off_t typedef +- Try to use _vsnprintf where it supplants vsprintf [Vollant] +- Add some casts in inffast.c +- Enchance comments in zlib.h on what happens if gzprintf() tries to + write more than 4095 bytes before compression +- Remove unused state from inflateBackEnd() +- Remove exit(0) from minigzip.c, example.c +- Get rid of all those darn tabs +- Add "check" target to Makefile.in that does the same thing as "test" +- Add "mostlyclean" and "maintainer-clean" targets to Makefile.in +- Update contrib/inflate86 [Anderson] +- Update contrib/testzlib, contrib/vstudio, contrib/minizip [Vollant] +- Add msdos and win32 directories with makefiles [Truta] +- More additions and improvements to the FAQ + +Changes in 1.2.0 (9 March 2003) +- New and improved inflate code + - About 20% faster + - Does not allocate 32K window unless and until needed + - Automatically detects and decompresses gzip streams + - Raw inflate no longer needs an extra dummy byte at end + - Added inflateBack functions using a callback interface--even faster + than inflate, useful for file utilities (gzip, zip) + - Added inflateCopy() function to record state for random access on + externally generated deflate streams (e.g. in gzip files) + - More readable code (I hope) +- New and improved crc32() + - About 50% faster, thanks to suggestions from Rodney Brown +- Add deflateBound() and compressBound() functions +- Fix memory leak in deflateInit2() +- Permit setting dictionary for raw deflate (for parallel deflate) +- Fix const declaration for gzwrite() +- Check for some malloc() failures in gzio.c +- Fix bug in gzopen() on single-byte file 0x1f +- Fix bug in gzread() on concatenated file with 0x1f at end of buffer + and next buffer doesn't start with 0x8b +- Fix uncompress() to return Z_DATA_ERROR on truncated input +- Free memory at end of example.c +- Remove MAX #define in trees.c (conflicted with some libraries) +- Fix static const's in deflate.c, gzio.c, and zutil.[ch] +- Declare malloc() and free() in gzio.c if STDC not defined +- Use malloc() instead of calloc() in zutil.c if int big enough +- Define STDC for AIX +- Add aix/ with approach for compiling shared library on AIX +- Add HP-UX support for shared libraries in configure +- Add OpenUNIX support for shared libraries in configure +- Use $cc instead of gcc to build shared library +- Make prefix directory if needed when installing +- Correct Macintosh avoidance of typedef Byte in zconf.h +- Correct Turbo C memory allocation when under Linux +- Use libz.a instead of -lz in Makefile (assure use of compiled library) +- Update configure to check for snprintf or vsnprintf functions and their + return value, warn during make if using an insecure function +- Fix configure problem with compile-time knowledge of HAVE_UNISTD_H that + is lost when library is used--resolution is to build new zconf.h +- Documentation improvements (in zlib.h): + - Document raw deflate and inflate + - Update RFCs URL + - Point out that zlib and gzip formats are different + - Note that Z_BUF_ERROR is not fatal + - Document string limit for gzprintf() and possible buffer overflow + - Note requirement on avail_out when flushing + - Note permitted values of flush parameter of inflate() +- Add some FAQs (and even answers) to the FAQ +- Add contrib/inflate86/ for x86 faster inflate +- Add contrib/blast/ for PKWare Data Compression Library decompression +- Add contrib/puff/ simple inflate for deflate format description + +Changes in 1.1.4 (11 March 2002) +- ZFREE was repeated on same allocation on some error conditions. + This creates a security problem described in + http://www.zlib.org/advisory-2002-03-11.txt +- Returned incorrect error (Z_MEM_ERROR) on some invalid data +- Avoid accesses before window for invalid distances with inflate window + less than 32K. +- force windowBits > 8 to avoid a bug in the encoder for a window size + of 256 bytes. (A complete fix will be available in 1.1.5). + +Changes in 1.1.3 (9 July 1998) +- fix "an inflate input buffer bug that shows up on rare but persistent + occasions" (Mark) +- fix gzread and gztell for concatenated .gz files (Didier Le Botlan) +- fix gzseek(..., SEEK_SET) in write mode +- fix crc check after a gzeek (Frank Faubert) +- fix miniunzip when the last entry in a zip file is itself a zip file + (J Lillge) +- add contrib/asm586 and contrib/asm686 (Brian Raiter) + See http://www.muppetlabs.com/~breadbox/software/assembly.html +- add support for Delphi 3 in contrib/delphi (Bob Dellaca) +- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti) +- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren) +- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks) +- added a FAQ file + +- Support gzdopen on Mac with Metrowerks (Jason Linhart) +- Do not redefine Byte on Mac (Brad Pettit & Jason Linhart) +- define SEEK_END too if SEEK_SET is not defined (Albert Chin-A-Young) +- avoid some warnings with Borland C (Tom Tanner) +- fix a problem in contrib/minizip/zip.c for 16-bit MSDOS (Gilles Vollant) +- emulate utime() for WIN32 in contrib/untgz (Gilles Vollant) +- allow several arguments to configure (Tim Mooney, Frodo Looijaard) +- use libdir and includedir in Makefile.in (Tim Mooney) +- support shared libraries on OSF1 V4 (Tim Mooney) +- remove so_locations in "make clean" (Tim Mooney) +- fix maketree.c compilation error (Glenn, Mark) +- Python interface to zlib now in Python 1.5 (Jeremy Hylton) +- new Makefile.riscos (Rich Walker) +- initialize static descriptors in trees.c for embedded targets (Nick Smith) +- use "foo-gz" in example.c for RISCOS and VMS (Nick Smith) +- add the OS/2 files in Makefile.in too (Andrew Zabolotny) +- fix fdopen and halloc macros for Microsoft C 6.0 (Tom Lane) +- fix maketree.c to allow clean compilation of inffixed.h (Mark) +- fix parameter check in deflateCopy (Gunther Nikl) +- cleanup trees.c, use compressed_len only in debug mode (Christian Spieler) +- Many portability patches by Christian Spieler: + . zutil.c, zutil.h: added "const" for zmem* + . Make_vms.com: fixed some typos + . Make_vms.com: msdos/Makefile.*: removed zutil.h from some dependency lists + . msdos/Makefile.msc: remove "default rtl link library" info from obj files + . msdos/Makefile.*: use model-dependent name for the built zlib library + . msdos/Makefile.emx, nt/Makefile.emx, nt/Makefile.gcc: + new makefiles, for emx (DOS/OS2), emx&rsxnt and mingw32 (Windows 9x / NT) +- use define instead of typedef for Bytef also for MSC small/medium (Tom Lane) +- replace __far with _far for better portability (Christian Spieler, Tom Lane) +- fix test for errno.h in configure (Tim Newsham) + +Changes in 1.1.2 (19 March 98) +- added contrib/minzip, mini zip and unzip based on zlib (Gilles Vollant) + See http://www.winimage.com/zLibDll/unzip.html +- preinitialize the inflate tables for fixed codes, to make the code + completely thread safe (Mark) +- some simplifications and slight speed-up to the inflate code (Mark) +- fix gzeof on non-compressed files (Allan Schrum) +- add -std1 option in configure for OSF1 to fix gzprintf (Martin Mokrejs) +- use default value of 4K for Z_BUFSIZE for 16-bit MSDOS (Tim Wegner + Glenn) +- added os2/Makefile.def and os2/zlib.def (Andrew Zabolotny) +- add shared lib support for UNIX_SV4.2MP (MATSUURA Takanori) +- do not wrap extern "C" around system includes (Tom Lane) +- mention zlib binding for TCL in README (Andreas Kupries) +- added amiga/Makefile.pup for Amiga powerUP SAS/C PPC (Andreas Kleinert) +- allow "make install prefix=..." even after configure (Glenn Randers-Pehrson) +- allow "configure --prefix $HOME" (Tim Mooney) +- remove warnings in example.c and gzio.c (Glenn Randers-Pehrson) +- move Makefile.sas to amiga/Makefile.sas + +Changes in 1.1.1 (27 Feb 98) +- fix macros _tr_tally_* in deflate.h for debug mode (Glenn Randers-Pehrson) +- remove block truncation heuristic which had very marginal effect for zlib + (smaller lit_bufsize than in gzip 1.2.4) and degraded a little the + compression ratio on some files. This also allows inlining _tr_tally for + matches in deflate_slow. +- added msdos/Makefile.w32 for WIN32 Microsoft Visual C++ (Bob Frazier) + +Changes in 1.1.0 (24 Feb 98) +- do not return STREAM_END prematurely in inflate (John Bowler) +- revert to the zlib 1.0.8 inflate to avoid the gcc 2.8.0 bug (Jeremy Buhler) +- compile with -DFASTEST to get compression code optimized for speed only +- in minigzip, try mmap'ing the input file first (Miguel Albrecht) +- increase size of I/O buffers in minigzip.c and gzio.c (not a big gain + on Sun but significant on HP) + +- add a pointer to experimental unzip library in README (Gilles Vollant) +- initialize variable gcc in configure (Chris Herborth) + +Changes in 1.0.9 (17 Feb 1998) +- added gzputs and gzgets functions +- do not clear eof flag in gzseek (Mark Diekhans) +- fix gzseek for files in transparent mode (Mark Diekhans) +- do not assume that vsprintf returns the number of bytes written (Jens Krinke) +- replace EXPORT with ZEXPORT to avoid conflict with other programs +- added compress2 in zconf.h, zlib.def, zlib.dnt +- new asm code from Gilles Vollant in contrib/asm386 +- simplify the inflate code (Mark): + . Replace ZALLOC's in huft_build() with single ZALLOC in inflate_blocks_new() + . ZALLOC the length list in inflate_trees_fixed() instead of using stack + . ZALLOC the value area for huft_build() instead of using stack + . Simplify Z_FINISH check in inflate() + +- Avoid gcc 2.8.0 comparison bug a little differently than zlib 1.0.8 +- in inftrees.c, avoid cc -O bug on HP (Farshid Elahi) +- in zconf.h move the ZLIB_DLL stuff earlier to avoid problems with + the declaration of FAR (Gilles VOllant) +- install libz.so* with mode 755 (executable) instead of 644 (Marc Lehmann) +- read_buf buf parameter of type Bytef* instead of charf* +- zmemcpy parameters are of type Bytef*, not charf* (Joseph Strout) +- do not redeclare unlink in minigzip.c for WIN32 (John Bowler) +- fix check for presence of directories in "make install" (Ian Willis) + +Changes in 1.0.8 (27 Jan 1998) +- fixed offsets in contrib/asm386/gvmat32.asm (Gilles Vollant) +- fix gzgetc and gzputc for big endian systems (Markus Oberhumer) +- added compress2() to allow setting the compression level +- include sys/types.h to get off_t on some systems (Marc Lehmann & QingLong) +- use constant arrays for the static trees in trees.c instead of computing + them at run time (thanks to Ken Raeburn for this suggestion). To create + trees.h, compile with GEN_TREES_H and run "make test". +- check return code of example in "make test" and display result +- pass minigzip command line options to file_compress +- simplifying code of inflateSync to avoid gcc 2.8 bug + +- support CC="gcc -Wall" in configure -s (QingLong) +- avoid a flush caused by ftell in gzopen for write mode (Ken Raeburn) +- fix test for shared library support to avoid compiler warnings +- zlib.lib -> zlib.dll in msdos/zlib.rc (Gilles Vollant) +- check for TARGET_OS_MAC in addition to MACOS (Brad Pettit) +- do not use fdopen for Metrowerks on Mac (Brad Pettit)) +- add checks for gzputc and gzputc in example.c +- avoid warnings in gzio.c and deflate.c (Andreas Kleinert) +- use const for the CRC table (Ken Raeburn) +- fixed "make uninstall" for shared libraries +- use Tracev instead of Trace in infblock.c +- in example.c use correct compressed length for test_sync +- suppress +vnocompatwarnings in configure for HPUX (not always supported) + +Changes in 1.0.7 (20 Jan 1998) +- fix gzseek which was broken in write mode +- return error for gzseek to negative absolute position +- fix configure for Linux (Chun-Chung Chen) +- increase stack space for MSC (Tim Wegner) +- get_crc_table and inflateSyncPoint are EXPORTed (Gilles Vollant) +- define EXPORTVA for gzprintf (Gilles Vollant) +- added man page zlib.3 (Rick Rodgers) +- for contrib/untgz, fix makedir() and improve Makefile + +- check gzseek in write mode in example.c +- allocate extra buffer for seeks only if gzseek is actually called +- avoid signed/unsigned comparisons (Tim Wegner, Gilles Vollant) +- add inflateSyncPoint in zconf.h +- fix list of exported functions in nt/zlib.dnt and mdsos/zlib.def + +Changes in 1.0.6 (19 Jan 1998) +- add functions gzprintf, gzputc, gzgetc, gztell, gzeof, gzseek, gzrewind and + gzsetparams (thanks to Roland Giersig and Kevin Ruland for some of this code) +- Fix a deflate bug occurring only with compression level 0 (thanks to + Andy Buckler for finding this one). +- In minigzip, pass transparently also the first byte for .Z files. +- return Z_BUF_ERROR instead of Z_OK if output buffer full in uncompress() +- check Z_FINISH in inflate (thanks to Marc Schluper) +- Implement deflateCopy (thanks to Adam Costello) +- make static libraries by default in configure, add --shared option. +- move MSDOS or Windows specific files to directory msdos +- suppress the notion of partial flush to simplify the interface + (but the symbol Z_PARTIAL_FLUSH is kept for compatibility with 1.0.4) +- suppress history buffer provided by application to simplify the interface + (this feature was not implemented anyway in 1.0.4) +- next_in and avail_in must be initialized before calling inflateInit or + inflateInit2 +- add EXPORT in all exported functions (for Windows DLL) +- added Makefile.nt (thanks to Stephen Williams) +- added the unsupported "contrib" directory: + contrib/asm386/ by Gilles Vollant + 386 asm code replacing longest_match(). + contrib/iostream/ by Kevin Ruland + A C++ I/O streams interface to the zlib gz* functions + contrib/iostream2/ by Tyge Løvset + Another C++ I/O streams interface + contrib/untgz/ by "Pedro A. Aranda Guti\irrez" + A very simple tar.gz file extractor using zlib + contrib/visual-basic.txt by Carlos Rios + How to use compress(), uncompress() and the gz* functions from VB. +- pass params -f (filtered data), -h (huffman only), -1 to -9 (compression + level) in minigzip (thanks to Tom Lane) + +- use const for rommable constants in deflate +- added test for gzseek and gztell in example.c +- add undocumented function inflateSyncPoint() (hack for Paul Mackerras) +- add undocumented function zError to convert error code to string + (for Tim Smithers) +- Allow compilation of gzio with -DNO_DEFLATE to avoid the compression code. +- Use default memcpy for Symantec MSDOS compiler. +- Add EXPORT keyword for check_func (needed for Windows DLL) +- add current directory to LD_LIBRARY_PATH for "make test" +- create also a link for libz.so.1 +- added support for FUJITSU UXP/DS (thanks to Toshiaki Nomura) +- use $(SHAREDLIB) instead of libz.so in Makefile.in (for HPUX) +- added -soname for Linux in configure (Chun-Chung Chen, +- assign numbers to the exported functions in zlib.def (for Windows DLL) +- add advice in zlib.h for best usage of deflateSetDictionary +- work around compiler bug on Atari (cast Z_NULL in call of s->checkfn) +- allow compilation with ANSI keywords only enabled for TurboC in large model +- avoid "versionString"[0] (Borland bug) +- add NEED_DUMMY_RETURN for Borland +- use variable z_verbose for tracing in debug mode (L. Peter Deutsch). +- allow compilation with CC +- defined STDC for OS/2 (David Charlap) +- limit external names to 8 chars for MVS (Thomas Lund) +- in minigzip.c, use static buffers only for 16-bit systems +- fix suffix check for "minigzip -d foo.gz" +- do not return an error for the 2nd of two consecutive gzflush() (Felix Lee) +- use _fdopen instead of fdopen for MSC >= 6.0 (Thomas Fanslau) +- added makelcc.bat for lcc-win32 (Tom St Denis) +- in Makefile.dj2, use copy and del instead of install and rm (Frank Donahoe) +- Avoid expanded $Id$. Use "rcs -kb" or "cvs admin -kb" to avoid Id expansion. +- check for unistd.h in configure (for off_t) +- remove useless check parameter in inflate_blocks_free +- avoid useless assignment of s->check to itself in inflate_blocks_new +- do not flush twice in gzclose (thanks to Ken Raeburn) +- rename FOPEN as F_OPEN to avoid clash with /usr/include/sys/file.h +- use NO_ERRNO_H instead of enumeration of operating systems with errno.h +- work around buggy fclose on pipes for HP/UX +- support zlib DLL with BORLAND C++ 5.0 (thanks to Glenn Randers-Pehrson) +- fix configure if CC is already equal to gcc + +Changes in 1.0.5 (3 Jan 98) +- Fix inflate to terminate gracefully when fed corrupted or invalid data +- Use const for rommable constants in inflate +- Eliminate memory leaks on error conditions in inflate +- Removed some vestigial code in inflate +- Update web address in README + +Changes in 1.0.4 (24 Jul 96) +- In very rare conditions, deflate(s, Z_FINISH) could fail to produce an EOF + bit, so the decompressor could decompress all the correct data but went + on to attempt decompressing extra garbage data. This affected minigzip too. +- zlibVersion and gzerror return const char* (needed for DLL) +- port to RISCOS (no fdopen, no multiple dots, no unlink, no fileno) +- use z_error only for DEBUG (avoid problem with DLLs) + +Changes in 1.0.3 (2 Jul 96) +- use z_streamp instead of z_stream *, which is now a far pointer in MSDOS + small and medium models; this makes the library incompatible with previous + versions for these models. (No effect in large model or on other systems.) +- return OK instead of BUF_ERROR if previous deflate call returned with + avail_out as zero but there is nothing to do +- added memcmp for non STDC compilers +- define NO_DUMMY_DECL for more Mac compilers (.h files merged incorrectly) +- define __32BIT__ if __386__ or i386 is defined (pb. with Watcom and SCO) +- better check for 16-bit mode MSC (avoids problem with Symantec) + +Changes in 1.0.2 (23 May 96) +- added Windows DLL support +- added a function zlibVersion (for the DLL support) +- fixed declarations using Bytef in infutil.c (pb with MSDOS medium model) +- Bytef is define's instead of typedef'd only for Borland C +- avoid reading uninitialized memory in example.c +- mention in README that the zlib format is now RFC1950 +- updated Makefile.dj2 +- added algorithm.doc + +Changes in 1.0.1 (20 May 96) [1.0 skipped to avoid confusion] +- fix array overlay in deflate.c which sometimes caused bad compressed data +- fix inflate bug with empty stored block +- fix MSDOS medium model which was broken in 0.99 +- fix deflateParams() which could generate bad compressed data. +- Bytef is define'd instead of typedef'ed (work around Borland bug) +- added an INDEX file +- new makefiles for DJGPP (Makefile.dj2), 32-bit Borland (Makefile.b32), + Watcom (Makefile.wat), Amiga SAS/C (Makefile.sas) +- speed up adler32 for modern machines without auto-increment +- added -ansi for IRIX in configure +- static_init_done in trees.c is an int +- define unlink as delete for VMS +- fix configure for QNX +- add configure branch for SCO and HPUX +- avoid many warnings (unused variables, dead assignments, etc...) +- no fdopen for BeOS +- fix the Watcom fix for 32 bit mode (define FAR as empty) +- removed redefinition of Byte for MKWERKS +- work around an MWKERKS bug (incorrect merge of all .h files) + +Changes in 0.99 (27 Jan 96) +- allow preset dictionary shared between compressor and decompressor +- allow compression level 0 (no compression) +- add deflateParams in zlib.h: allow dynamic change of compression level + and compression strategy. +- test large buffers and deflateParams in example.c +- add optional "configure" to build zlib as a shared library +- suppress Makefile.qnx, use configure instead +- fixed deflate for 64-bit systems (detected on Cray) +- fixed inflate_blocks for 64-bit systems (detected on Alpha) +- declare Z_DEFLATED in zlib.h (possible parameter for deflateInit2) +- always return Z_BUF_ERROR when deflate() has nothing to do +- deflateInit and inflateInit are now macros to allow version checking +- prefix all global functions and types with z_ with -DZ_PREFIX +- make falloc completely reentrant (inftrees.c) +- fixed very unlikely race condition in ct_static_init +- free in reverse order of allocation to help memory manager +- use zlib-1.0/* instead of zlib/* inside the tar.gz +- make zlib warning-free with "gcc -O3 -Wall -Wwrite-strings -Wpointer-arith + -Wconversion -Wstrict-prototypes -Wmissing-prototypes" +- allow gzread on concatenated .gz files +- deflateEnd now returns Z_DATA_ERROR if it was premature +- deflate is finally (?) fully deterministic (no matches beyond end of input) +- Document Z_SYNC_FLUSH +- add uninstall in Makefile +- Check for __cpluplus in zlib.h +- Better test in ct_align for partial flush +- avoid harmless warnings for Borland C++ +- initialize hash_head in deflate.c +- avoid warning on fdopen (gzio.c) for HP cc -Aa +- include stdlib.h for STDC compilers +- include errno.h for Cray +- ignore error if ranlib doesn't exist +- call ranlib twice for NeXTSTEP +- use exec_prefix instead of prefix for libz.a +- renamed ct_* as _tr_* to avoid conflict with applications +- clear z->msg in inflateInit2 before any error return +- initialize opaque in example.c, gzio.c, deflate.c and inflate.c +- fixed typo in zconf.h (_GNUC__ => __GNUC__) +- check for WIN32 in zconf.h and zutil.c (avoid farmalloc in 32-bit mode) +- fix typo in Make_vms.com (f$trnlnm -> f$getsyi) +- in fcalloc, normalize pointer if size > 65520 bytes +- don't use special fcalloc for 32 bit Borland C++ +- use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc... +- use Z_BINARY instead of BINARY +- document that gzclose after gzdopen will close the file +- allow "a" as mode in gzopen. +- fix error checking in gzread +- allow skipping .gz extra-field on pipes +- added reference to Perl interface in README +- put the crc table in FAR data (I dislike more and more the medium model :) +- added get_crc_table +- added a dimension to all arrays (Borland C can't count). +- workaround Borland C bug in declaration of inflate_codes_new & inflate_fast +- guard against multiple inclusion of *.h (for precompiled header on Mac) +- Watcom C pretends to be Microsoft C small model even in 32 bit mode. +- don't use unsized arrays to avoid silly warnings by Visual C++: + warning C4746: 'inflate_mask' : unsized array treated as '__far' + (what's wrong with far data in far model?). +- define enum out of inflate_blocks_state to allow compilation with C++ + +Changes in 0.95 (16 Aug 95) +- fix MSDOS small and medium model (now easier to adapt to any compiler) +- inlined send_bits +- fix the final (:-) bug for deflate with flush (output was correct but + not completely flushed in rare occasions). +- default window size is same for compression and decompression + (it's now sufficient to set MAX_WBITS in zconf.h). +- voidp -> voidpf and voidnp -> voidp (for consistency with other + typedefs and because voidnp was not near in large model). + +Changes in 0.94 (13 Aug 95) +- support MSDOS medium model +- fix deflate with flush (could sometimes generate bad output) +- fix deflateReset (zlib header was incorrectly suppressed) +- added support for VMS +- allow a compression level in gzopen() +- gzflush now calls fflush +- For deflate with flush, flush even if no more input is provided. +- rename libgz.a as libz.a +- avoid complex expression in infcodes.c triggering Turbo C bug +- work around a problem with gcc on Alpha (in INSERT_STRING) +- don't use inline functions (problem with some gcc versions) +- allow renaming of Byte, uInt, etc... with #define. +- avoid warning about (unused) pointer before start of array in deflate.c +- avoid various warnings in gzio.c, example.c, infblock.c, adler32.c, zutil.c +- avoid reserved word 'new' in trees.c + +Changes in 0.93 (25 June 95) +- temporarily disable inline functions +- make deflate deterministic +- give enough lookahead for PARTIAL_FLUSH +- Set binary mode for stdin/stdout in minigzip.c for OS/2 +- don't even use signed char in inflate (not portable enough) +- fix inflate memory leak for segmented architectures + +Changes in 0.92 (3 May 95) +- don't assume that char is signed (problem on SGI) +- Clear bit buffer when starting a stored block +- no memcpy on Pyramid +- suppressed inftest.c +- optimized fill_window, put longest_match inline for gcc +- optimized inflate on stored blocks. +- untabify all sources to simplify patches + +Changes in 0.91 (2 May 95) +- Default MEM_LEVEL is 8 (not 9 for Unix) as documented in zlib.h +- Document the memory requirements in zconf.h +- added "make install" +- fix sync search logic in inflateSync +- deflate(Z_FULL_FLUSH) now works even if output buffer too short +- after inflateSync, don't scare people with just "lo world" +- added support for DJGPP + +Changes in 0.9 (1 May 95) +- don't assume that zalloc clears the allocated memory (the TurboC bug + was Mark's bug after all :) +- let again gzread copy uncompressed data unchanged (was working in 0.71) +- deflate(Z_FULL_FLUSH), inflateReset and inflateSync are now fully implemented +- added a test of inflateSync in example.c +- moved MAX_WBITS to zconf.h because users might want to change that. +- document explicitly that zalloc(64K) on MSDOS must return a normalized + pointer (zero offset) +- added Makefiles for Microsoft C, Turbo C, Borland C++ +- faster crc32() + +Changes in 0.8 (29 April 95) +- added fast inflate (inffast.c) +- deflate(Z_FINISH) now returns Z_STREAM_END when done. Warning: this + is incompatible with previous versions of zlib which returned Z_OK. +- work around a TurboC compiler bug (bad code for b << 0, see infutil.h) + (actually that was not a compiler bug, see 0.81 above) +- gzread no longer reads one extra byte in certain cases +- In gzio destroy(), don't reference a freed structure +- avoid many warnings for MSDOS +- avoid the ERROR symbol which is used by MS Windows + +Changes in 0.71 (14 April 95) +- Fixed more MSDOS compilation problems :( There is still a bug with + TurboC large model. + +Changes in 0.7 (14 April 95) +- Added full inflate support. +- Simplified the crc32() interface. The pre- and post-conditioning + (one's complement) is now done inside crc32(). WARNING: this is + incompatible with previous versions; see zlib.h for the new usage. + +Changes in 0.61 (12 April 95) +- workaround for a bug in TurboC. example and minigzip now work on MSDOS. + +Changes in 0.6 (11 April 95) +- added minigzip.c +- added gzdopen to reopen a file descriptor as gzFile +- added transparent reading of non-gziped files in gzread. +- fixed bug in gzread (don't read crc as data) +- fixed bug in destroy (gzio.c) (don't return Z_STREAM_END for gzclose). +- don't allocate big arrays in the stack (for MSDOS) +- fix some MSDOS compilation problems + +Changes in 0.5: +- do real compression in deflate.c. Z_PARTIAL_FLUSH is supported but + not yet Z_FULL_FLUSH. +- support decompression but only in a single step (forced Z_FINISH) +- added opaque object for zalloc and zfree. +- added deflateReset and inflateReset +- added a variable zlib_version for consistency checking. +- renamed the 'filter' parameter of deflateInit2 as 'strategy'. + Added Z_FILTERED and Z_HUFFMAN_ONLY constants. + +Changes in 0.4: +- avoid "zip" everywhere, use zlib instead of ziplib. +- suppress Z_BLOCK_FLUSH, interpret Z_PARTIAL_FLUSH as block flush + if compression method == 8. +- added adler32 and crc32 +- renamed deflateOptions as deflateInit2, call one or the other but not both +- added the method parameter for deflateInit2. +- added inflateInit2 +- simplied considerably deflateInit and inflateInit by not supporting + user-provided history buffer. This is supported only in deflateInit2 + and inflateInit2. + +Changes in 0.3: +- prefix all macro names with Z_ +- use Z_FINISH instead of deflateEnd to finish compression. +- added Z_HUFFMAN_ONLY +- added gzerror() diff --git a/contrib/zlib/FAQ b/contrib/zlib/FAQ new file mode 100644 index 000000000..99b7cf92e --- /dev/null +++ b/contrib/zlib/FAQ @@ -0,0 +1,368 @@ + + Frequently Asked Questions about zlib + + +If your question is not there, please check the zlib home page +http://zlib.net/ which may have more recent information. +The lastest zlib FAQ is at http://zlib.net/zlib_faq.html + + + 1. Is zlib Y2K-compliant? + + Yes. zlib doesn't handle dates. + + 2. Where can I get a Windows DLL version? + + The zlib sources can be compiled without change to produce a DLL. See the + file win32/DLL_FAQ.txt in the zlib distribution. Pointers to the + precompiled DLL are found in the zlib web site at http://zlib.net/ . + + 3. Where can I get a Visual Basic interface to zlib? + + See + * http://marknelson.us/1997/01/01/zlib-engine/ + * win32/DLL_FAQ.txt in the zlib distribution + + 4. compress() returns Z_BUF_ERROR. + + Make sure that before the call of compress(), the length of the compressed + buffer is equal to the available size of the compressed buffer and not + zero. For Visual Basic, check that this parameter is passed by reference + ("as any"), not by value ("as long"). + + 5. deflate() or inflate() returns Z_BUF_ERROR. + + Before making the call, make sure that avail_in and avail_out are not zero. + When setting the parameter flush equal to Z_FINISH, also make sure that + avail_out is big enough to allow processing all pending input. Note that a + Z_BUF_ERROR is not fatal--another call to deflate() or inflate() can be + made with more input or output space. A Z_BUF_ERROR may in fact be + unavoidable depending on how the functions are used, since it is not + possible to tell whether or not there is more output pending when + strm.avail_out returns with zero. See http://zlib.net/zlib_how.html for a + heavily annotated example. + + 6. Where's the zlib documentation (man pages, etc.)? + + It's in zlib.h . Examples of zlib usage are in the files test/example.c + and test/minigzip.c, with more in examples/ . + + 7. Why don't you use GNU autoconf or libtool or ...? + + Because we would like to keep zlib as a very small and simple package. + zlib is rather portable and doesn't need much configuration. + + 8. I found a bug in zlib. + + Most of the time, such problems are due to an incorrect usage of zlib. + Please try to reproduce the problem with a small program and send the + corresponding source to us at zlib@gzip.org . Do not send multi-megabyte + data files without prior agreement. + + 9. Why do I get "undefined reference to gzputc"? + + If "make test" produces something like + + example.o(.text+0x154): undefined reference to `gzputc' + + check that you don't have old files libz.* in /usr/lib, /usr/local/lib or + /usr/X11R6/lib. Remove any old versions, then do "make install". + +10. I need a Delphi interface to zlib. + + See the contrib/delphi directory in the zlib distribution. + +11. Can zlib handle .zip archives? + + Not by itself, no. See the directory contrib/minizip in the zlib + distribution. + +12. Can zlib handle .Z files? + + No, sorry. You have to spawn an uncompress or gunzip subprocess, or adapt + the code of uncompress on your own. + +13. How can I make a Unix shared library? + + By default a shared (and a static) library is built for Unix. So: + + make distclean + ./configure + make + +14. How do I install a shared zlib library on Unix? + + After the above, then: + + make install + + However, many flavors of Unix come with a shared zlib already installed. + Before going to the trouble of compiling a shared version of zlib and + trying to install it, you may want to check if it's already there! If you + can #include , it's there. The -lz option will probably link to + it. You can check the version at the top of zlib.h or with the + ZLIB_VERSION symbol defined in zlib.h . + +15. I have a question about OttoPDF. + + We are not the authors of OttoPDF. The real author is on the OttoPDF web + site: Joel Hainley, jhainley@myndkryme.com. + +16. Can zlib decode Flate data in an Adobe PDF file? + + Yes. See http://www.pdflib.com/ . To modify PDF forms, see + http://sourceforge.net/projects/acroformtool/ . + +17. Why am I getting this "register_frame_info not found" error on Solaris? + + After installing zlib 1.1.4 on Solaris 2.6, running applications using zlib + generates an error such as: + + ld.so.1: rpm: fatal: relocation error: file /usr/local/lib/libz.so: + symbol __register_frame_info: referenced symbol not found + + The symbol __register_frame_info is not part of zlib, it is generated by + the C compiler (cc or gcc). You must recompile applications using zlib + which have this problem. This problem is specific to Solaris. See + http://www.sunfreeware.com for Solaris versions of zlib and applications + using zlib. + +18. Why does gzip give an error on a file I make with compress/deflate? + + The compress and deflate functions produce data in the zlib format, which + is different and incompatible with the gzip format. The gz* functions in + zlib on the other hand use the gzip format. Both the zlib and gzip formats + use the same compressed data format internally, but have different headers + and trailers around the compressed data. + +19. Ok, so why are there two different formats? + + The gzip format was designed to retain the directory information about a + single file, such as the name and last modification date. The zlib format + on the other hand was designed for in-memory and communication channel + applications, and has a much more compact header and trailer and uses a + faster integrity check than gzip. + +20. Well that's nice, but how do I make a gzip file in memory? + + You can request that deflate write the gzip format instead of the zlib + format using deflateInit2(). You can also request that inflate decode the + gzip format using inflateInit2(). Read zlib.h for more details. + +21. Is zlib thread-safe? + + Yes. However any library routines that zlib uses and any application- + provided memory allocation routines must also be thread-safe. zlib's gz* + functions use stdio library routines, and most of zlib's functions use the + library memory allocation routines by default. zlib's *Init* functions + allow for the application to provide custom memory allocation routines. + + Of course, you should only operate on any given zlib or gzip stream from a + single thread at a time. + +22. Can I use zlib in my commercial application? + + Yes. Please read the license in zlib.h. + +23. Is zlib under the GNU license? + + No. Please read the license in zlib.h. + +24. The license says that altered source versions must be "plainly marked". So + what exactly do I need to do to meet that requirement? + + You need to change the ZLIB_VERSION and ZLIB_VERNUM #defines in zlib.h. In + particular, the final version number needs to be changed to "f", and an + identification string should be appended to ZLIB_VERSION. Version numbers + x.x.x.f are reserved for modifications to zlib by others than the zlib + maintainers. For example, if the version of the base zlib you are altering + is "1.2.3.4", then in zlib.h you should change ZLIB_VERNUM to 0x123f, and + ZLIB_VERSION to something like "1.2.3.f-zachary-mods-v3". You can also + update the version strings in deflate.c and inftrees.c. + + For altered source distributions, you should also note the origin and + nature of the changes in zlib.h, as well as in ChangeLog and README, along + with the dates of the alterations. The origin should include at least your + name (or your company's name), and an email address to contact for help or + issues with the library. + + Note that distributing a compiled zlib library along with zlib.h and + zconf.h is also a source distribution, and so you should change + ZLIB_VERSION and ZLIB_VERNUM and note the origin and nature of the changes + in zlib.h as you would for a full source distribution. + +25. Will zlib work on a big-endian or little-endian architecture, and can I + exchange compressed data between them? + + Yes and yes. + +26. Will zlib work on a 64-bit machine? + + Yes. It has been tested on 64-bit machines, and has no dependence on any + data types being limited to 32-bits in length. If you have any + difficulties, please provide a complete problem report to zlib@gzip.org + +27. Will zlib decompress data from the PKWare Data Compression Library? + + No. The PKWare DCL uses a completely different compressed data format than + does PKZIP and zlib. However, you can look in zlib's contrib/blast + directory for a possible solution to your problem. + +28. Can I access data randomly in a compressed stream? + + No, not without some preparation. If when compressing you periodically use + Z_FULL_FLUSH, carefully write all the pending data at those points, and + keep an index of those locations, then you can start decompression at those + points. You have to be careful to not use Z_FULL_FLUSH too often, since it + can significantly degrade compression. Alternatively, you can scan a + deflate stream once to generate an index, and then use that index for + random access. See examples/zran.c . + +29. Does zlib work on MVS, OS/390, CICS, etc.? + + It has in the past, but we have not heard of any recent evidence. There + were working ports of zlib 1.1.4 to MVS, but those links no longer work. + If you know of recent, successful applications of zlib on these operating + systems, please let us know. Thanks. + +30. Is there some simpler, easier to read version of inflate I can look at to + understand the deflate format? + + First off, you should read RFC 1951. Second, yes. Look in zlib's + contrib/puff directory. + +31. Does zlib infringe on any patents? + + As far as we know, no. In fact, that was originally the whole point behind + zlib. Look here for some more information: + + http://www.gzip.org/#faq11 + +32. Can zlib work with greater than 4 GB of data? + + Yes. inflate() and deflate() will process any amount of data correctly. + Each call of inflate() or deflate() is limited to input and output chunks + of the maximum value that can be stored in the compiler's "unsigned int" + type, but there is no limit to the number of chunks. Note however that the + strm.total_in and strm_total_out counters may be limited to 4 GB. These + counters are provided as a convenience and are not used internally by + inflate() or deflate(). The application can easily set up its own counters + updated after each call of inflate() or deflate() to count beyond 4 GB. + compress() and uncompress() may be limited to 4 GB, since they operate in a + single call. gzseek() and gztell() may be limited to 4 GB depending on how + zlib is compiled. See the zlibCompileFlags() function in zlib.h. + + The word "may" appears several times above since there is a 4 GB limit only + if the compiler's "long" type is 32 bits. If the compiler's "long" type is + 64 bits, then the limit is 16 exabytes. + +33. Does zlib have any security vulnerabilities? + + The only one that we are aware of is potentially in gzprintf(). If zlib is + compiled to use sprintf() or vsprintf(), then there is no protection + against a buffer overflow of an 8K string space (or other value as set by + gzbuffer()), other than the caller of gzprintf() assuring that the output + will not exceed 8K. On the other hand, if zlib is compiled to use + snprintf() or vsnprintf(), which should normally be the case, then there is + no vulnerability. The ./configure script will display warnings if an + insecure variation of sprintf() will be used by gzprintf(). Also the + zlibCompileFlags() function will return information on what variant of + sprintf() is used by gzprintf(). + + If you don't have snprintf() or vsnprintf() and would like one, you can + find a portable implementation here: + + http://www.ijs.si/software/snprintf/ + + Note that you should be using the most recent version of zlib. Versions + 1.1.3 and before were subject to a double-free vulnerability, and versions + 1.2.1 and 1.2.2 were subject to an access exception when decompressing + invalid compressed data. + +34. Is there a Java version of zlib? + + Probably what you want is to use zlib in Java. zlib is already included + as part of the Java SDK in the java.util.zip package. If you really want + a version of zlib written in the Java language, look on the zlib home + page for links: http://zlib.net/ . + +35. I get this or that compiler or source-code scanner warning when I crank it + up to maximally-pedantic. Can't you guys write proper code? + + Many years ago, we gave up attempting to avoid warnings on every compiler + in the universe. It just got to be a waste of time, and some compilers + were downright silly as well as contradicted each other. So now, we simply + make sure that the code always works. + +36. Valgrind (or some similar memory access checker) says that deflate is + performing a conditional jump that depends on an uninitialized value. + Isn't that a bug? + + No. That is intentional for performance reasons, and the output of deflate + is not affected. This only started showing up recently since zlib 1.2.x + uses malloc() by default for allocations, whereas earlier versions used + calloc(), which zeros out the allocated memory. Even though the code was + correct, versions 1.2.4 and later was changed to not stimulate these + checkers. + +37. Will zlib read the (insert any ancient or arcane format here) compressed + data format? + + Probably not. Look in the comp.compression FAQ for pointers to various + formats and associated software. + +38. How can I encrypt/decrypt zip files with zlib? + + zlib doesn't support encryption. The original PKZIP encryption is very + weak and can be broken with freely available programs. To get strong + encryption, use GnuPG, http://www.gnupg.org/ , which already includes zlib + compression. For PKZIP compatible "encryption", look at + http://www.info-zip.org/ + +39. What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings? + + "gzip" is the gzip format, and "deflate" is the zlib format. They should + probably have called the second one "zlib" instead to avoid confusion with + the raw deflate compressed data format. While the HTTP 1.1 RFC 2616 + correctly points to the zlib specification in RFC 1950 for the "deflate" + transfer encoding, there have been reports of servers and browsers that + incorrectly produce or expect raw deflate data per the deflate + specification in RFC 1951, most notably Microsoft. So even though the + "deflate" transfer encoding using the zlib format would be the more + efficient approach (and in fact exactly what the zlib format was designed + for), using the "gzip" transfer encoding is probably more reliable due to + an unfortunate choice of name on the part of the HTTP 1.1 authors. + + Bottom line: use the gzip format for HTTP 1.1 encoding. + +40. Does zlib support the new "Deflate64" format introduced by PKWare? + + No. PKWare has apparently decided to keep that format proprietary, since + they have not documented it as they have previous compression formats. In + any case, the compression improvements are so modest compared to other more + modern approaches, that it's not worth the effort to implement. + +41. I'm having a problem with the zip functions in zlib, can you help? + + There are no zip functions in zlib. You are probably using minizip by + Giles Vollant, which is found in the contrib directory of zlib. It is not + part of zlib. In fact none of the stuff in contrib is part of zlib. The + files in there are not supported by the zlib authors. You need to contact + the authors of the respective contribution for help. + +42. The match.asm code in contrib is under the GNU General Public License. + Since it's part of zlib, doesn't that mean that all of zlib falls under the + GNU GPL? + + No. The files in contrib are not part of zlib. They were contributed by + other authors and are provided as a convenience to the user within the zlib + distribution. Each item in contrib has its own license. + +43. Is zlib subject to export controls? What is its ECCN? + + zlib is not subject to export controls, and so is classified as EAR99. + +44. Can you please sign these lengthy legal documents and fax them back to us + so that we can use your software in our product? + + No. Go away. Shoo. diff --git a/contrib/zlib/INDEX b/contrib/zlib/INDEX new file mode 100644 index 000000000..2ba064120 --- /dev/null +++ b/contrib/zlib/INDEX @@ -0,0 +1,68 @@ +CMakeLists.txt cmake build file +ChangeLog history of changes +FAQ Frequently Asked Questions about zlib +INDEX this file +Makefile dummy Makefile that tells you to ./configure +Makefile.in template for Unix Makefile +README guess what +configure configure script for Unix +make_vms.com makefile for VMS +test/example.c zlib usages examples for build testing +test/minigzip.c minimal gzip-like functionality for build testing +test/infcover.c inf*.c code coverage for build coverage testing +treebuild.xml XML description of source file dependencies +zconf.h.cmakein zconf.h template for cmake +zconf.h.in zconf.h template for configure +zlib.3 Man page for zlib +zlib.3.pdf Man page in PDF format +zlib.map Linux symbol information +zlib.pc.in Template for pkg-config descriptor +zlib.pc.cmakein zlib.pc template for cmake +zlib2ansi perl script to convert source files for C++ compilation + +amiga/ makefiles for Amiga SAS C +as400/ makefiles for AS/400 +doc/ documentation for formats and algorithms +msdos/ makefiles for MSDOS +nintendods/ makefile for Nintendo DS +old/ makefiles for various architectures and zlib documentation + files that have not yet been updated for zlib 1.2.x +qnx/ makefiles for QNX +watcom/ makefiles for OpenWatcom +win32/ makefiles for Windows + + zlib public header files (required for library use): +zconf.h +zlib.h + + private source files used to build the zlib library: +adler32.c +compress.c +crc32.c +crc32.h +deflate.c +deflate.h +gzclose.c +gzguts.h +gzlib.c +gzread.c +gzwrite.c +infback.c +inffast.c +inffast.h +inffixed.h +inflate.c +inflate.h +inftrees.c +inftrees.h +trees.c +trees.h +uncompr.c +zutil.c +zutil.h + + source files for sample programs +See examples/README.examples + + unsupported contributions by third parties +See contrib/README.contrib diff --git a/contrib/zlib/Makefile.in b/contrib/zlib/Makefile.in new file mode 100644 index 000000000..3413266ae --- /dev/null +++ b/contrib/zlib/Makefile.in @@ -0,0 +1,410 @@ +# Makefile for zlib +# Copyright (C) 1995-2017 Jean-loup Gailly, Mark Adler +# For conditions of distribution and use, see copyright notice in zlib.h + +# To compile and test, type: +# ./configure; make test +# Normally configure builds both a static and a shared library. +# If you want to build just a static library, use: ./configure --static + +# To use the asm code, type: +# cp contrib/asm?86/match.S ./match.S +# make LOC=-DASMV OBJA=match.o + +# To install /usr/local/lib/libz.* and /usr/local/include/zlib.h, type: +# make install +# To install in $HOME instead of /usr/local, use: +# make install prefix=$HOME + +CC=cc + +CFLAGS=-O +#CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7 +#CFLAGS=-g -DZLIB_DEBUG +#CFLAGS=-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \ +# -Wstrict-prototypes -Wmissing-prototypes + +SFLAGS=-O +LDFLAGS= +TEST_LDFLAGS=-L. libz.a +LDSHARED=$(CC) +CPP=$(CC) -E + +STATICLIB=libz.a +SHAREDLIB=libz.so +SHAREDLIBV=libz.so.1.2.11.1 +SHAREDLIBM=libz.so.1 +LIBS=$(STATICLIB) $(SHAREDLIBV) + +AR=ar +ARFLAGS=rc +RANLIB=ranlib +LDCONFIG=ldconfig +LDSHAREDLIBC=-lc +TAR=tar +SHELL=/bin/sh +EXE= + +prefix = /usr/local +exec_prefix = ${prefix} +libdir = ${exec_prefix}/lib +sharedlibdir = ${libdir} +includedir = ${prefix}/include +mandir = ${prefix}/share/man +man3dir = ${mandir}/man3 +pkgconfigdir = ${libdir}/pkgconfig +SRCDIR= +ZINC= +ZINCOUT=-I. + +OBJZ = adler32.o crc32.o deflate.o infback.o inffast.o inflate.o inftrees.o trees.o zutil.o +OBJG = compress.o uncompr.o gzclose.o gzlib.o gzread.o gzwrite.o +OBJC = $(OBJZ) $(OBJG) + +PIC_OBJZ = adler32.lo crc32.lo deflate.lo infback.lo inffast.lo inflate.lo inftrees.lo trees.lo zutil.lo +PIC_OBJG = compress.lo uncompr.lo gzclose.lo gzlib.lo gzread.lo gzwrite.lo +PIC_OBJC = $(PIC_OBJZ) $(PIC_OBJG) + +# to use the asm code: make OBJA=match.o, PIC_OBJA=match.lo +OBJA = +PIC_OBJA = + +OBJS = $(OBJC) $(OBJA) + +PIC_OBJS = $(PIC_OBJC) $(PIC_OBJA) + +all: static shared + +static: example$(EXE) minigzip$(EXE) + +shared: examplesh$(EXE) minigzipsh$(EXE) + +all64: example64$(EXE) minigzip64$(EXE) + +check: test + +test: all teststatic testshared + +teststatic: static + @TMPST=tmpst_$$; \ + if echo hello world | ./minigzip | ./minigzip -d && ./example $$TMPST ; then \ + echo ' *** zlib test OK ***'; \ + else \ + echo ' *** zlib test FAILED ***'; false; \ + fi + @rm -f tmpst_$$ + +testshared: shared + @LD_LIBRARY_PATH=`pwd`:$(LD_LIBRARY_PATH) ; export LD_LIBRARY_PATH; \ + LD_LIBRARYN32_PATH=`pwd`:$(LD_LIBRARYN32_PATH) ; export LD_LIBRARYN32_PATH; \ + DYLD_LIBRARY_PATH=`pwd`:$(DYLD_LIBRARY_PATH) ; export DYLD_LIBRARY_PATH; \ + SHLIB_PATH=`pwd`:$(SHLIB_PATH) ; export SHLIB_PATH; \ + TMPSH=tmpsh_$$; \ + if echo hello world | ./minigzipsh | ./minigzipsh -d && ./examplesh $$TMPSH; then \ + echo ' *** zlib shared test OK ***'; \ + else \ + echo ' *** zlib shared test FAILED ***'; false; \ + fi + @rm -f tmpsh_$$ + +test64: all64 + @TMP64=tmp64_$$; \ + if echo hello world | ./minigzip64 | ./minigzip64 -d && ./example64 $$TMP64; then \ + echo ' *** zlib 64-bit test OK ***'; \ + else \ + echo ' *** zlib 64-bit test FAILED ***'; false; \ + fi + @rm -f tmp64_$$ + +infcover.o: $(SRCDIR)test/infcover.c $(SRCDIR)zlib.h zconf.h + $(CC) $(CFLAGS) $(ZINCOUT) -c -o $@ $(SRCDIR)test/infcover.c + +infcover: infcover.o libz.a + $(CC) $(CFLAGS) -o $@ infcover.o libz.a + +cover: infcover + rm -f *.gcda + ./infcover + gcov inf*.c + +libz.a: $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + -@ ($(RANLIB) $@ || true) >/dev/null 2>&1 + +match.o: match.S + $(CPP) match.S > _match.s + $(CC) -c _match.s + mv _match.o match.o + rm -f _match.s + +match.lo: match.S + $(CPP) match.S > _match.s + $(CC) -c -fPIC _match.s + mv _match.o match.lo + rm -f _match.s + +example.o: $(SRCDIR)test/example.c $(SRCDIR)zlib.h zconf.h + $(CC) $(CFLAGS) $(ZINCOUT) -c -o $@ $(SRCDIR)test/example.c + +minigzip.o: $(SRCDIR)test/minigzip.c $(SRCDIR)zlib.h zconf.h + $(CC) $(CFLAGS) $(ZINCOUT) -c -o $@ $(SRCDIR)test/minigzip.c + +example64.o: $(SRCDIR)test/example.c $(SRCDIR)zlib.h zconf.h + $(CC) $(CFLAGS) $(ZINCOUT) -D_FILE_OFFSET_BITS=64 -c -o $@ $(SRCDIR)test/example.c + +minigzip64.o: $(SRCDIR)test/minigzip.c $(SRCDIR)zlib.h zconf.h + $(CC) $(CFLAGS) $(ZINCOUT) -D_FILE_OFFSET_BITS=64 -c -o $@ $(SRCDIR)test/minigzip.c + + +adler32.o: $(SRCDIR)adler32.c + $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)adler32.c + +crc32.o: $(SRCDIR)crc32.c + $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)crc32.c + +deflate.o: $(SRCDIR)deflate.c + $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)deflate.c + +infback.o: $(SRCDIR)infback.c + $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)infback.c + +inffast.o: $(SRCDIR)inffast.c + $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)inffast.c + +inflate.o: $(SRCDIR)inflate.c + $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)inflate.c + +inftrees.o: $(SRCDIR)inftrees.c + $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)inftrees.c + +trees.o: $(SRCDIR)trees.c + $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)trees.c + +zutil.o: $(SRCDIR)zutil.c + $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)zutil.c + +compress.o: $(SRCDIR)compress.c + $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)compress.c + +uncompr.o: $(SRCDIR)uncompr.c + $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)uncompr.c + +gzclose.o: $(SRCDIR)gzclose.c + $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)gzclose.c + +gzlib.o: $(SRCDIR)gzlib.c + $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)gzlib.c + +gzread.o: $(SRCDIR)gzread.c + $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)gzread.c + +gzwrite.o: $(SRCDIR)gzwrite.c + $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)gzwrite.c + + +adler32.lo: $(SRCDIR)adler32.c + -@mkdir objs 2>/dev/null || test -d objs + $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/adler32.o $(SRCDIR)adler32.c + -@mv objs/adler32.o $@ + +crc32.lo: $(SRCDIR)crc32.c + -@mkdir objs 2>/dev/null || test -d objs + $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/crc32.o $(SRCDIR)crc32.c + -@mv objs/crc32.o $@ + +deflate.lo: $(SRCDIR)deflate.c + -@mkdir objs 2>/dev/null || test -d objs + $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/deflate.o $(SRCDIR)deflate.c + -@mv objs/deflate.o $@ + +infback.lo: $(SRCDIR)infback.c + -@mkdir objs 2>/dev/null || test -d objs + $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/infback.o $(SRCDIR)infback.c + -@mv objs/infback.o $@ + +inffast.lo: $(SRCDIR)inffast.c + -@mkdir objs 2>/dev/null || test -d objs + $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/inffast.o $(SRCDIR)inffast.c + -@mv objs/inffast.o $@ + +inflate.lo: $(SRCDIR)inflate.c + -@mkdir objs 2>/dev/null || test -d objs + $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/inflate.o $(SRCDIR)inflate.c + -@mv objs/inflate.o $@ + +inftrees.lo: $(SRCDIR)inftrees.c + -@mkdir objs 2>/dev/null || test -d objs + $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/inftrees.o $(SRCDIR)inftrees.c + -@mv objs/inftrees.o $@ + +trees.lo: $(SRCDIR)trees.c + -@mkdir objs 2>/dev/null || test -d objs + $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/trees.o $(SRCDIR)trees.c + -@mv objs/trees.o $@ + +zutil.lo: $(SRCDIR)zutil.c + -@mkdir objs 2>/dev/null || test -d objs + $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/zutil.o $(SRCDIR)zutil.c + -@mv objs/zutil.o $@ + +compress.lo: $(SRCDIR)compress.c + -@mkdir objs 2>/dev/null || test -d objs + $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/compress.o $(SRCDIR)compress.c + -@mv objs/compress.o $@ + +uncompr.lo: $(SRCDIR)uncompr.c + -@mkdir objs 2>/dev/null || test -d objs + $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/uncompr.o $(SRCDIR)uncompr.c + -@mv objs/uncompr.o $@ + +gzclose.lo: $(SRCDIR)gzclose.c + -@mkdir objs 2>/dev/null || test -d objs + $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/gzclose.o $(SRCDIR)gzclose.c + -@mv objs/gzclose.o $@ + +gzlib.lo: $(SRCDIR)gzlib.c + -@mkdir objs 2>/dev/null || test -d objs + $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/gzlib.o $(SRCDIR)gzlib.c + -@mv objs/gzlib.o $@ + +gzread.lo: $(SRCDIR)gzread.c + -@mkdir objs 2>/dev/null || test -d objs + $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/gzread.o $(SRCDIR)gzread.c + -@mv objs/gzread.o $@ + +gzwrite.lo: $(SRCDIR)gzwrite.c + -@mkdir objs 2>/dev/null || test -d objs + $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/gzwrite.o $(SRCDIR)gzwrite.c + -@mv objs/gzwrite.o $@ + + +placebo $(SHAREDLIBV): $(PIC_OBJS) libz.a + $(LDSHARED) $(SFLAGS) -o $@ $(PIC_OBJS) $(LDSHAREDLIBC) $(LDFLAGS) + rm -f $(SHAREDLIB) $(SHAREDLIBM) + ln -s $@ $(SHAREDLIB) + ln -s $@ $(SHAREDLIBM) + -@rmdir objs + +example$(EXE): example.o $(STATICLIB) + $(CC) $(CFLAGS) -o $@ example.o $(TEST_LDFLAGS) + +minigzip$(EXE): minigzip.o $(STATICLIB) + $(CC) $(CFLAGS) -o $@ minigzip.o $(TEST_LDFLAGS) + +examplesh$(EXE): example.o $(SHAREDLIBV) + $(CC) $(CFLAGS) -o $@ example.o -L. $(SHAREDLIBV) + +minigzipsh$(EXE): minigzip.o $(SHAREDLIBV) + $(CC) $(CFLAGS) -o $@ minigzip.o -L. $(SHAREDLIBV) + +example64$(EXE): example64.o $(STATICLIB) + $(CC) $(CFLAGS) -o $@ example64.o $(TEST_LDFLAGS) + +minigzip64$(EXE): minigzip64.o $(STATICLIB) + $(CC) $(CFLAGS) -o $@ minigzip64.o $(TEST_LDFLAGS) + +install-libs: $(LIBS) + -@if [ ! -d $(DESTDIR)$(exec_prefix) ]; then mkdir -p $(DESTDIR)$(exec_prefix); fi + -@if [ ! -d $(DESTDIR)$(libdir) ]; then mkdir -p $(DESTDIR)$(libdir); fi + -@if [ ! -d $(DESTDIR)$(sharedlibdir) ]; then mkdir -p $(DESTDIR)$(sharedlibdir); fi + -@if [ ! -d $(DESTDIR)$(man3dir) ]; then mkdir -p $(DESTDIR)$(man3dir); fi + -@if [ ! -d $(DESTDIR)$(pkgconfigdir) ]; then mkdir -p $(DESTDIR)$(pkgconfigdir); fi + rm -f $(DESTDIR)$(libdir)/$(STATICLIB) + cp $(STATICLIB) $(DESTDIR)$(libdir) + chmod 644 $(DESTDIR)$(libdir)/$(STATICLIB) + -@($(RANLIB) $(DESTDIR)$(libdir)/libz.a || true) >/dev/null 2>&1 + -@if test -n "$(SHAREDLIBV)"; then \ + rm -f $(DESTDIR)$(sharedlibdir)/$(SHAREDLIBV); \ + cp $(SHAREDLIBV) $(DESTDIR)$(sharedlibdir); \ + echo "cp $(SHAREDLIBV) $(DESTDIR)$(sharedlibdir)"; \ + chmod 755 $(DESTDIR)$(sharedlibdir)/$(SHAREDLIBV); \ + echo "chmod 755 $(DESTDIR)$(sharedlibdir)/$(SHAREDLIBV)"; \ + rm -f $(DESTDIR)$(sharedlibdir)/$(SHAREDLIB) $(DESTDIR)$(sharedlibdir)/$(SHAREDLIBM); \ + ln -s $(SHAREDLIBV) $(DESTDIR)$(sharedlibdir)/$(SHAREDLIB); \ + ln -s $(SHAREDLIBV) $(DESTDIR)$(sharedlibdir)/$(SHAREDLIBM); \ + ($(LDCONFIG) || true) >/dev/null 2>&1; \ + fi + rm -f $(DESTDIR)$(man3dir)/zlib.3 + cp $(SRCDIR)zlib.3 $(DESTDIR)$(man3dir) + chmod 644 $(DESTDIR)$(man3dir)/zlib.3 + rm -f $(DESTDIR)$(pkgconfigdir)/zlib.pc + cp zlib.pc $(DESTDIR)$(pkgconfigdir) + chmod 644 $(DESTDIR)$(pkgconfigdir)/zlib.pc +# The ranlib in install is needed on NeXTSTEP which checks file times +# ldconfig is for Linux + +install: install-libs + -@if [ ! -d $(DESTDIR)$(includedir) ]; then mkdir -p $(DESTDIR)$(includedir); fi + rm -f $(DESTDIR)$(includedir)/zlib.h $(DESTDIR)$(includedir)/zconf.h + cp $(SRCDIR)zlib.h zconf.h $(DESTDIR)$(includedir) + chmod 644 $(DESTDIR)$(includedir)/zlib.h $(DESTDIR)$(includedir)/zconf.h + +uninstall: + cd $(DESTDIR)$(includedir) && rm -f zlib.h zconf.h + cd $(DESTDIR)$(libdir) && rm -f libz.a; \ + if test -n "$(SHAREDLIBV)" -a -f $(SHAREDLIBV); then \ + rm -f $(SHAREDLIBV) $(SHAREDLIB) $(SHAREDLIBM); \ + fi + cd $(DESTDIR)$(man3dir) && rm -f zlib.3 + cd $(DESTDIR)$(pkgconfigdir) && rm -f zlib.pc + +docs: zlib.3.pdf + +zlib.3.pdf: $(SRCDIR)zlib.3 + groff -mandoc -f H -T ps $(SRCDIR)zlib.3 | ps2pdf - $@ + +zconf.h.cmakein: $(SRCDIR)zconf.h.in + -@ TEMPFILE=zconfh_$$; \ + echo "/#define ZCONF_H/ a\\\\\n#cmakedefine Z_PREFIX\\\\\n#cmakedefine Z_HAVE_UNISTD_H\n" >> $$TEMPFILE &&\ + sed -f $$TEMPFILE $(SRCDIR)zconf.h.in > $@ &&\ + touch -r $(SRCDIR)zconf.h.in $@ &&\ + rm $$TEMPFILE + +zconf: $(SRCDIR)zconf.h.in + cp -p $(SRCDIR)zconf.h.in zconf.h + +mostlyclean: clean +clean: + rm -f *.o *.lo *~ \ + example$(EXE) minigzip$(EXE) examplesh$(EXE) minigzipsh$(EXE) \ + example64$(EXE) minigzip64$(EXE) \ + infcover \ + libz.* foo.gz so_locations \ + _match.s maketree contrib/infback9/*.o + rm -rf objs + rm -f *.gcda *.gcno *.gcov + rm -f contrib/infback9/*.gcda contrib/infback9/*.gcno contrib/infback9/*.gcov + +maintainer-clean: distclean +distclean: clean zconf zconf.h.cmakein docs + rm -f Makefile zlib.pc configure.log + -@rm -f .DS_Store + @if [ -f Makefile.in ]; then \ + printf 'all:\n\t-@echo "Please use ./configure first. Thank you."\n' > Makefile ; \ + printf '\ndistclean:\n\tmake -f Makefile.in distclean\n' >> Makefile ; \ + touch -r $(SRCDIR)Makefile.in Makefile ; fi + @if [ ! -f zconf.h.in ]; then rm -f zconf.h zconf.h.cmakein ; fi + @if [ ! -f zlib.3 ]; then rm -f zlib.3.pdf ; fi + +tags: + etags $(SRCDIR)*.[ch] + +adler32.o zutil.o: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h +gzclose.o gzlib.o gzread.o gzwrite.o: $(SRCDIR)zlib.h zconf.h $(SRCDIR)gzguts.h +compress.o example.o minigzip.o uncompr.o: $(SRCDIR)zlib.h zconf.h +crc32.o: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)crc32.h +deflate.o: $(SRCDIR)deflate.h $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h +infback.o inflate.o: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h $(SRCDIR)inflate.h $(SRCDIR)inffast.h $(SRCDIR)inffixed.h +inffast.o: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h $(SRCDIR)inflate.h $(SRCDIR)inffast.h +inftrees.o: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h +trees.o: $(SRCDIR)deflate.h $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)trees.h + +adler32.lo zutil.lo: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h +gzclose.lo gzlib.lo gzread.lo gzwrite.lo: $(SRCDIR)zlib.h zconf.h $(SRCDIR)gzguts.h +compress.lo example.lo minigzip.lo uncompr.lo: $(SRCDIR)zlib.h zconf.h +crc32.lo: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)crc32.h +deflate.lo: $(SRCDIR)deflate.h $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h +infback.lo inflate.lo: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h $(SRCDIR)inflate.h $(SRCDIR)inffast.h $(SRCDIR)inffixed.h +inffast.lo: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h $(SRCDIR)inflate.h $(SRCDIR)inffast.h +inftrees.lo: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h +trees.lo: $(SRCDIR)deflate.h $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)trees.h diff --git a/contrib/zlib/README b/contrib/zlib/README index 5ca9d127e..41777d06d 100644 --- a/contrib/zlib/README +++ b/contrib/zlib/README @@ -1,6 +1,6 @@ ZLIB DATA COMPRESSION LIBRARY -zlib 1.2.8 is a general purpose data compression library. All the code is +zlib 1.2.11.1 is a general purpose data compression library. All the code is thread safe. The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 (zlib format), rfc1951 (deflate format) and @@ -31,7 +31,7 @@ Mark Nelson wrote an article about zlib for the Jan. 1997 issue of Dr. Dobb's Journal; a copy of the article is available at http://marknelson.us/1997/01/01/zlib-engine/ . -The changes made in version 1.2.8 are documented in the file ChangeLog. +The changes made in version 1.2.11.1 are documented in the file ChangeLog. Unsupported third party contributions are provided in directory contrib/ . @@ -84,7 +84,7 @@ Acknowledgments: Copyright notice: - (C) 1995-2013 Jean-loup Gailly and Mark Adler + (C) 1995-2017 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/contrib/zlib/adler32.c b/contrib/zlib/adler32.c index a868f073d..d0be4380a 100644 --- a/contrib/zlib/adler32.c +++ b/contrib/zlib/adler32.c @@ -1,5 +1,5 @@ /* adler32.c -- compute the Adler-32 checksum of a data stream - * Copyright (C) 1995-2011 Mark Adler + * Copyright (C) 1995-2011, 2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -7,11 +7,9 @@ #include "zutil.h" -#define local static - local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); -#define BASE 65521 /* largest prime smaller than 65536 */ +#define BASE 65521U /* largest prime smaller than 65536 */ #define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ @@ -62,10 +60,10 @@ local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); #endif /* ========================================================================= */ -uLong ZEXPORT adler32(adler, buf, len) +uLong ZEXPORT adler32_z(adler, buf, len) uLong adler; const Bytef *buf; - uInt len; + z_size_t len; { unsigned long sum2; unsigned n; @@ -132,6 +130,15 @@ uLong ZEXPORT adler32(adler, buf, len) return adler | (sum2 << 16); } +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + return adler32_z(adler, buf, len); +} + /* ========================================================================= */ local uLong adler32_combine_(adler1, adler2, len2) uLong adler1; @@ -156,7 +163,7 @@ local uLong adler32_combine_(adler1, adler2, len2) sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; if (sum1 >= BASE) sum1 -= BASE; if (sum1 >= BASE) sum1 -= BASE; - if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 >= ((unsigned long)BASE << 1)) sum2 -= ((unsigned long)BASE << 1); if (sum2 >= BASE) sum2 -= BASE; return sum1 | (sum2 << 16); } diff --git a/contrib/zlib/compress.c b/contrib/zlib/compress.c index 6e9762676..e2db404ab 100644 --- a/contrib/zlib/compress.c +++ b/contrib/zlib/compress.c @@ -1,5 +1,5 @@ /* compress.c -- compress a memory buffer - * Copyright (C) 1995-2005 Jean-loup Gailly. + * Copyright (C) 1995-2005, 2014, 2016 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -28,16 +28,11 @@ int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) { z_stream stream; int err; + const uInt max = (uInt)-1; + uLong left; - stream.next_in = (z_const Bytef *)source; - stream.avail_in = (uInt)sourceLen; -#ifdef MAXSEG_64K - /* Check for source > 64K on 16-bit machine: */ - if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; -#endif - stream.next_out = dest; - stream.avail_out = (uInt)*destLen; - if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + left = *destLen; + *destLen = 0; stream.zalloc = (alloc_func)0; stream.zfree = (free_func)0; @@ -46,15 +41,26 @@ int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) err = deflateInit(&stream, level); if (err != Z_OK) return err; - err = deflate(&stream, Z_FINISH); - if (err != Z_STREAM_END) { - deflateEnd(&stream); - return err == Z_OK ? Z_BUF_ERROR : err; - } - *destLen = stream.total_out; + stream.next_out = dest; + stream.avail_out = 0; + stream.next_in = (z_const Bytef *)source; + stream.avail_in = 0; - err = deflateEnd(&stream); - return err; + do { + if (stream.avail_out == 0) { + stream.avail_out = left > (uLong)max ? max : (uInt)left; + left -= stream.avail_out; + } + if (stream.avail_in == 0) { + stream.avail_in = sourceLen > (uLong)max ? max : (uInt)sourceLen; + sourceLen -= stream.avail_in; + } + err = deflate(&stream, sourceLen ? Z_NO_FLUSH : Z_FINISH); + } while (err == Z_OK); + + *destLen = stream.total_out; + deflateEnd(&stream); + return err == Z_STREAM_END ? Z_OK : err; } /* =========================================================================== diff --git a/contrib/zlib/configure b/contrib/zlib/configure new file mode 100644 index 000000000..f4127954c --- /dev/null +++ b/contrib/zlib/configure @@ -0,0 +1,921 @@ +#!/bin/sh +# configure script for zlib. +# +# Normally configure builds both a static and a shared library. +# If you want to build just a static library, use: ./configure --static +# +# To impose specific compiler or flags or install directory, use for example: +# prefix=$HOME CC=cc CFLAGS="-O4" ./configure +# or for csh/tcsh users: +# (setenv prefix $HOME; setenv CC cc; setenv CFLAGS "-O4"; ./configure) + +# Incorrect settings of CC or CFLAGS may prevent creating a shared library. +# If you have problems, try without defining CC and CFLAGS before reporting +# an error. + +# start off configure.log +echo -------------------- >> configure.log +echo $0 $* >> configure.log +date >> configure.log + +# get source directory +SRCDIR=`dirname $0` +if test $SRCDIR = "."; then + ZINC="" + ZINCOUT="-I." + SRCDIR="" +else + ZINC='-include zconf.h' + ZINCOUT='-I. -I$(SRCDIR)' + SRCDIR="$SRCDIR/" +fi + +# set command prefix for cross-compilation +if [ -n "${CHOST}" ]; then + uname="`echo "${CHOST}" | sed -e 's/^[^-]*-\([^-]*\)$/\1/' -e 's/^[^-]*-[^-]*-\([^-]*\)$/\1/' -e 's/^[^-]*-[^-]*-\([^-]*\)-.*$/\1/'`" + CROSS_PREFIX="${CHOST}-" +fi + +# destination name for static library +STATICLIB=libz.a + +# extract zlib version numbers from zlib.h +VER=`sed -n -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < ${SRCDIR}zlib.h` +VER3=`sed -n -e '/VERSION "/s/.*"\([0-9]*\\.[0-9]*\\.[0-9]*\).*/\1/p' < ${SRCDIR}zlib.h` +VER2=`sed -n -e '/VERSION "/s/.*"\([0-9]*\\.[0-9]*\)\\..*/\1/p' < ${SRCDIR}zlib.h` +VER1=`sed -n -e '/VERSION "/s/.*"\([0-9]*\)\\..*/\1/p' < ${SRCDIR}zlib.h` + +# establish commands for library building +if "${CROSS_PREFIX}ar" --version >/dev/null 2>/dev/null || test $? -lt 126; then + AR=${AR-"${CROSS_PREFIX}ar"} + test -n "${CROSS_PREFIX}" && echo Using ${AR} | tee -a configure.log +else + AR=${AR-"ar"} + test -n "${CROSS_PREFIX}" && echo Using ${AR} | tee -a configure.log +fi +ARFLAGS=${ARFLAGS-"rc"} +if "${CROSS_PREFIX}ranlib" --version >/dev/null 2>/dev/null || test $? -lt 126; then + RANLIB=${RANLIB-"${CROSS_PREFIX}ranlib"} + test -n "${CROSS_PREFIX}" && echo Using ${RANLIB} | tee -a configure.log +else + RANLIB=${RANLIB-"ranlib"} +fi +if "${CROSS_PREFIX}nm" --version >/dev/null 2>/dev/null || test $? -lt 126; then + NM=${NM-"${CROSS_PREFIX}nm"} + test -n "${CROSS_PREFIX}" && echo Using ${NM} | tee -a configure.log +else + NM=${NM-"nm"} +fi + +# set defaults before processing command line options +LDCONFIG=${LDCONFIG-"ldconfig"} +LDSHAREDLIBC="${LDSHAREDLIBC--lc}" +ARCHS= +prefix=${prefix-/usr/local} +exec_prefix=${exec_prefix-'${prefix}'} +libdir=${libdir-'${exec_prefix}/lib'} +sharedlibdir=${sharedlibdir-'${libdir}'} +includedir=${includedir-'${prefix}/include'} +mandir=${mandir-'${prefix}/share/man'} +shared_ext='.so' +shared=1 +solo=0 +cover=0 +zprefix=0 +zconst=0 +build64=0 +gcc=0 +warn=0 +debug=0 +old_cc="$CC" +old_cflags="$CFLAGS" +OBJC='$(OBJZ) $(OBJG)' +PIC_OBJC='$(PIC_OBJZ) $(PIC_OBJG)' + +# leave this script, optionally in a bad way +leave() +{ + if test "$*" != "0"; then + echo "** $0 aborting." | tee -a configure.log + fi + rm -f $test.[co] $test $test$shared_ext $test.gcno ./--version + echo -------------------- >> configure.log + echo >> configure.log + echo >> configure.log + exit $1 +} + +# process command line options +while test $# -ge 1 +do +case "$1" in + -h* | --help) + echo 'usage:' | tee -a configure.log + echo ' configure [--const] [--zprefix] [--prefix=PREFIX] [--eprefix=EXPREFIX]' | tee -a configure.log + echo ' [--static] [--64] [--libdir=LIBDIR] [--sharedlibdir=LIBDIR]' | tee -a configure.log + echo ' [--includedir=INCLUDEDIR] [--archs="-arch i386 -arch x86_64"]' | tee -a configure.log + exit 0 ;; + -p*=* | --prefix=*) prefix=`echo $1 | sed 's/.*=//'`; shift ;; + -e*=* | --eprefix=*) exec_prefix=`echo $1 | sed 's/.*=//'`; shift ;; + -l*=* | --libdir=*) libdir=`echo $1 | sed 's/.*=//'`; shift ;; + --sharedlibdir=*) sharedlibdir=`echo $1 | sed 's/.*=//'`; shift ;; + -i*=* | --includedir=*) includedir=`echo $1 | sed 's/.*=//'`;shift ;; + -u*=* | --uname=*) uname=`echo $1 | sed 's/.*=//'`;shift ;; + -p* | --prefix) prefix="$2"; shift; shift ;; + -e* | --eprefix) exec_prefix="$2"; shift; shift ;; + -l* | --libdir) libdir="$2"; shift; shift ;; + -i* | --includedir) includedir="$2"; shift; shift ;; + -s* | --shared | --enable-shared) shared=1; shift ;; + -t | --static) shared=0; shift ;; + --solo) solo=1; shift ;; + --cover) cover=1; shift ;; + -z* | --zprefix) zprefix=1; shift ;; + -6* | --64) build64=1; shift ;; + -a*=* | --archs=*) ARCHS=`echo $1 | sed 's/.*=//'`; shift ;; + --sysconfdir=*) echo "ignored option: --sysconfdir" | tee -a configure.log; shift ;; + --localstatedir=*) echo "ignored option: --localstatedir" | tee -a configure.log; shift ;; + -c* | --const) zconst=1; shift ;; + -w* | --warn) warn=1; shift ;; + -d* | --debug) debug=1; shift ;; + *) + echo "unknown option: $1" | tee -a configure.log + echo "$0 --help for help" | tee -a configure.log + leave 1;; + esac +done + +# temporary file name +test=ztest$$ + +# put arguments in log, also put test file in log if used in arguments +show() +{ + case "$*" in + *$test.c*) + echo === $test.c === >> configure.log + cat $test.c >> configure.log + echo === >> configure.log;; + esac + echo $* >> configure.log +} + +# check for gcc vs. cc and set compile and link flags based on the system identified by uname +cat > $test.c <&1` in + *gcc*) gcc=1 ;; + *clang*) gcc=1 ;; +esac + +show $cc -c $test.c +if test "$gcc" -eq 1 && ($cc -c $test.c) >> configure.log 2>&1; then + echo ... using gcc >> configure.log + CC="$cc" + CFLAGS="${CFLAGS--O3}" + SFLAGS="${CFLAGS--O3} -fPIC" + if test "$ARCHS"; then + CFLAGS="${CFLAGS} ${ARCHS}" + LDFLAGS="${LDFLAGS} ${ARCHS}" + fi + if test $build64 -eq 1; then + CFLAGS="${CFLAGS} -m64" + SFLAGS="${SFLAGS} -m64" + fi + if test "$warn" -eq 1; then + if test "$zconst" -eq 1; then + CFLAGS="${CFLAGS} -Wall -Wextra -Wcast-qual -pedantic -DZLIB_CONST -fsanitize=address" + else + CFLAGS="${CFLAGS} -Wall -Wextra -pedantic -fsanitize=address" + fi + fi + if test $debug -eq 1; then + CFLAGS="${CFLAGS} -DZLIB_DEBUG" + SFLAGS="${SFLAGS} -DZLIB_DEBUG" + fi + if test -z "$uname"; then + uname=`(uname -s || echo unknown) 2>/dev/null` + fi + case "$uname" in + Linux* | linux* | GNU | GNU/* | solaris*) + LDSHARED=${LDSHARED-"$cc -shared -Wl,-soname,libz.so.1,--version-script,${SRCDIR}zlib.map"} ;; + *BSD | *bsd* | DragonFly) + LDSHARED=${LDSHARED-"$cc -shared -Wl,-soname,libz.so.1,--version-script,${SRCDIR}zlib.map"} + LDCONFIG="ldconfig -m" ;; + CYGWIN* | Cygwin* | cygwin* | OS/2*) + EXE='.exe' ;; + MINGW* | mingw*) +# temporary bypass + rm -f $test.[co] $test $test$shared_ext + echo "Please use win32/Makefile.gcc instead." | tee -a configure.log + leave 1 + LDSHARED=${LDSHARED-"$cc -shared"} + LDSHAREDLIBC="" + EXE='.exe' ;; + QNX*) # This is for QNX6. I suppose that the QNX rule below is for QNX2,QNX4 + # (alain.bonnefoy@icbt.com) + LDSHARED=${LDSHARED-"$cc -shared -Wl,-hlibz.so.1"} ;; + HP-UX*) + LDSHARED=${LDSHARED-"$cc -shared $SFLAGS"} + case `(uname -m || echo unknown) 2>/dev/null` in + ia64) + shared_ext='.so' + SHAREDLIB='libz.so' ;; + *) + shared_ext='.sl' + SHAREDLIB='libz.sl' ;; + esac ;; + Darwin* | darwin*) + shared_ext='.dylib' + SHAREDLIB=libz$shared_ext + SHAREDLIBV=libz.$VER$shared_ext + SHAREDLIBM=libz.$VER1$shared_ext + LDSHARED=${LDSHARED-"$cc -dynamiclib -install_name $libdir/$SHAREDLIBM -compatibility_version $VER1 -current_version $VER3"} + if libtool -V 2>&1 | grep Apple > /dev/null; then + AR="libtool" + else + AR="/usr/bin/libtool" + fi + ARFLAGS="-o" ;; + *) LDSHARED=${LDSHARED-"$cc -shared"} ;; + esac +else + # find system name and corresponding cc options + CC=${CC-cc} + gcc=0 + echo ... using $CC >> configure.log + if test -z "$uname"; then + uname=`(uname -sr || echo unknown) 2>/dev/null` + fi + case "$uname" in + HP-UX*) SFLAGS=${CFLAGS-"-O +z"} + CFLAGS=${CFLAGS-"-O"} +# LDSHARED=${LDSHARED-"ld -b +vnocompatwarnings"} + LDSHARED=${LDSHARED-"ld -b"} + case `(uname -m || echo unknown) 2>/dev/null` in + ia64) + shared_ext='.so' + SHAREDLIB='libz.so' ;; + *) + shared_ext='.sl' + SHAREDLIB='libz.sl' ;; + esac ;; + IRIX*) SFLAGS=${CFLAGS-"-ansi -O2 -rpath ."} + CFLAGS=${CFLAGS-"-ansi -O2"} + LDSHARED=${LDSHARED-"cc -shared -Wl,-soname,libz.so.1"} ;; + OSF1\ V4*) SFLAGS=${CFLAGS-"-O -std1"} + CFLAGS=${CFLAGS-"-O -std1"} + LDFLAGS="${LDFLAGS} -Wl,-rpath,." + LDSHARED=${LDSHARED-"cc -shared -Wl,-soname,libz.so -Wl,-msym -Wl,-rpath,$(libdir) -Wl,-set_version,${VER}:1.0"} ;; + OSF1*) SFLAGS=${CFLAGS-"-O -std1"} + CFLAGS=${CFLAGS-"-O -std1"} + LDSHARED=${LDSHARED-"cc -shared -Wl,-soname,libz.so.1"} ;; + QNX*) SFLAGS=${CFLAGS-"-4 -O"} + CFLAGS=${CFLAGS-"-4 -O"} + LDSHARED=${LDSHARED-"cc"} + RANLIB=${RANLIB-"true"} + AR="cc" + ARFLAGS="-A" ;; + SCO_SV\ 3.2*) SFLAGS=${CFLAGS-"-O3 -dy -KPIC "} + CFLAGS=${CFLAGS-"-O3"} + LDSHARED=${LDSHARED-"cc -dy -KPIC -G"} ;; + SunOS\ 5* | solaris*) + LDSHARED=${LDSHARED-"cc -G -h libz$shared_ext.$VER1"} + SFLAGS=${CFLAGS-"-fast -KPIC"} + CFLAGS=${CFLAGS-"-fast"} + if test $build64 -eq 1; then + # old versions of SunPRO/Workshop/Studio don't support -m64, + # but newer ones do. Check for it. + flag64=`$CC -flags | egrep -- '^-m64'` + if test x"$flag64" != x"" ; then + CFLAGS="${CFLAGS} -m64" + SFLAGS="${SFLAGS} -m64" + else + case `(uname -m || echo unknown) 2>/dev/null` in + i86*) + SFLAGS="$SFLAGS -xarch=amd64" + CFLAGS="$CFLAGS -xarch=amd64" ;; + *) + SFLAGS="$SFLAGS -xarch=v9" + CFLAGS="$CFLAGS -xarch=v9" ;; + esac + fi + fi + if test -n "$ZINC"; then + ZINC='-I- -I. -I$(SRCDIR)' + fi + ;; + SunOS\ 4*) SFLAGS=${CFLAGS-"-O2 -PIC"} + CFLAGS=${CFLAGS-"-O2"} + LDSHARED=${LDSHARED-"ld"} ;; + SunStudio\ 9*) SFLAGS=${CFLAGS-"-fast -xcode=pic32 -xtarget=ultra3 -xarch=v9b"} + CFLAGS=${CFLAGS-"-fast -xtarget=ultra3 -xarch=v9b"} + LDSHARED=${LDSHARED-"cc -xarch=v9b"} ;; + UNIX_System_V\ 4.2.0) + SFLAGS=${CFLAGS-"-KPIC -O"} + CFLAGS=${CFLAGS-"-O"} + LDSHARED=${LDSHARED-"cc -G"} ;; + UNIX_SV\ 4.2MP) + SFLAGS=${CFLAGS-"-Kconform_pic -O"} + CFLAGS=${CFLAGS-"-O"} + LDSHARED=${LDSHARED-"cc -G"} ;; + OpenUNIX\ 5) + SFLAGS=${CFLAGS-"-KPIC -O"} + CFLAGS=${CFLAGS-"-O"} + LDSHARED=${LDSHARED-"cc -G"} ;; + AIX*) # Courtesy of dbakker@arrayasolutions.com + SFLAGS=${CFLAGS-"-O -qmaxmem=8192"} + CFLAGS=${CFLAGS-"-O -qmaxmem=8192"} + LDSHARED=${LDSHARED-"xlc -G"} ;; + # send working options for other systems to zlib@gzip.org + *) SFLAGS=${CFLAGS-"-O"} + CFLAGS=${CFLAGS-"-O"} + LDSHARED=${LDSHARED-"cc -shared"} ;; + esac +fi + +# destination names for shared library if not defined above +SHAREDLIB=${SHAREDLIB-"libz$shared_ext"} +SHAREDLIBV=${SHAREDLIBV-"libz$shared_ext.$VER"} +SHAREDLIBM=${SHAREDLIBM-"libz$shared_ext.$VER1"} + +echo >> configure.log + +# define functions for testing compiler and library characteristics and logging the results + +cat > $test.c </dev/null; then + try() + { + show $* + test "`( $* ) 2>&1 | tee -a configure.log`" = "" + } + echo - using any output from compiler to indicate an error >> configure.log +else + try() + { + show $* + ( $* ) >> configure.log 2>&1 + ret=$? + if test $ret -ne 0; then + echo "(exit code "$ret")" >> configure.log + fi + return $ret + } +fi + +tryboth() +{ + show $* + got=`( $* ) 2>&1` + ret=$? + printf %s "$got" >> configure.log + if test $ret -ne 0; then + return $ret + fi + test "$got" = "" +} + +cat > $test.c << EOF +int foo() { return 0; } +EOF +echo "Checking for obsessive-compulsive compiler options..." >> configure.log +if try $CC -c $CFLAGS $test.c; then + : +else + echo "Compiler error reporting is too harsh for $0 (perhaps remove -Werror)." | tee -a configure.log + leave 1 +fi + +echo >> configure.log + +# see if shared library build supported +cat > $test.c <> configure.log + show "$NM $test.o | grep _hello" + if test "`$NM $test.o | grep _hello | tee -a configure.log`" = ""; then + CPP="$CPP -DNO_UNDERLINE" + echo Checking for underline in external names... No. | tee -a configure.log + else + echo Checking for underline in external names... Yes. | tee -a configure.log + fi ;; +esac + +echo >> configure.log + +# check for size_t +cat > $test.c < +#include +size_t dummy = 0; +EOF +if try $CC -c $CFLAGS $test.c; then + echo "Checking for size_t... Yes." | tee -a configure.log + need_sizet=0 +else + echo "Checking for size_t... No." | tee -a configure.log + need_sizet=1 +fi + +echo >> configure.log + +# find the size_t integer type, if needed +if test $need_sizet -eq 1; then + cat > $test.c < $test.c < +int main(void) { + if (sizeof(void *) <= sizeof(int)) puts("int"); + else if (sizeof(void *) <= sizeof(long)) puts("long"); + else puts("z_longlong"); + return 0; +} +EOF + else + echo "Checking for long long... No." | tee -a configure.log + cat > $test.c < +int main(void) { + if (sizeof(void *) <= sizeof(int)) puts("int"); + else puts("long"); + return 0; +} +EOF + fi + if try $CC $CFLAGS -o $test $test.c; then + sizet=`./$test` + echo "Checking for a pointer-size integer type..." $sizet"." | tee -a configure.log + else + echo "Failed to find a pointer-size integer type." | tee -a configure.log + leave 1 + fi +fi + +if test $need_sizet -eq 1; then + CFLAGS="${CFLAGS} -DNO_SIZE_T=${sizet}" + SFLAGS="${SFLAGS} -DNO_SIZE_T=${sizet}" +fi + +echo >> configure.log + +# check for large file support, and if none, check for fseeko() +cat > $test.c < +off64_t dummy = 0; +EOF +if try $CC -c $CFLAGS -D_LARGEFILE64_SOURCE=1 $test.c; then + CFLAGS="${CFLAGS} -D_LARGEFILE64_SOURCE=1" + SFLAGS="${SFLAGS} -D_LARGEFILE64_SOURCE=1" + ALL="${ALL} all64" + TEST="${TEST} test64" + echo "Checking for off64_t... Yes." | tee -a configure.log + echo "Checking for fseeko... Yes." | tee -a configure.log +else + echo "Checking for off64_t... No." | tee -a configure.log + echo >> configure.log + cat > $test.c < +int main(void) { + fseeko(NULL, 0, 0); + return 0; +} +EOF + if try $CC $CFLAGS -o $test $test.c; then + echo "Checking for fseeko... Yes." | tee -a configure.log + else + CFLAGS="${CFLAGS} -DNO_FSEEKO" + SFLAGS="${SFLAGS} -DNO_FSEEKO" + echo "Checking for fseeko... No." | tee -a configure.log + fi +fi + +echo >> configure.log + +# check for strerror() for use by gz* functions +cat > $test.c < +#include +int main() { return strlen(strerror(errno)); } +EOF +if try $CC $CFLAGS -o $test $test.c; then + echo "Checking for strerror... Yes." | tee -a configure.log +else + CFLAGS="${CFLAGS} -DNO_STRERROR" + SFLAGS="${SFLAGS} -DNO_STRERROR" + echo "Checking for strerror... No." | tee -a configure.log +fi + +# copy clean zconf.h for subsequent edits +cp -p ${SRCDIR}zconf.h.in zconf.h + +echo >> configure.log + +# check for unistd.h and save result in zconf.h +cat > $test.c < +int main() { return 0; } +EOF +if try $CC -c $CFLAGS $test.c; then + sed < zconf.h "/^#ifdef HAVE_UNISTD_H.* may be/s/def HAVE_UNISTD_H\(.*\) may be/ 1\1 was/" > zconf.temp.h + mv zconf.temp.h zconf.h + echo "Checking for unistd.h... Yes." | tee -a configure.log +else + echo "Checking for unistd.h... No." | tee -a configure.log +fi + +echo >> configure.log + +# check for stdarg.h and save result in zconf.h +cat > $test.c < +int main() { return 0; } +EOF +if try $CC -c $CFLAGS $test.c; then + sed < zconf.h "/^#ifdef HAVE_STDARG_H.* may be/s/def HAVE_STDARG_H\(.*\) may be/ 1\1 was/" > zconf.temp.h + mv zconf.temp.h zconf.h + echo "Checking for stdarg.h... Yes." | tee -a configure.log +else + echo "Checking for stdarg.h... No." | tee -a configure.log +fi + +# if the z_ prefix was requested, save that in zconf.h +if test $zprefix -eq 1; then + sed < zconf.h "/#ifdef Z_PREFIX.* may be/s/def Z_PREFIX\(.*\) may be/ 1\1 was/" > zconf.temp.h + mv zconf.temp.h zconf.h + echo >> configure.log + echo "Using z_ prefix on all symbols." | tee -a configure.log +fi + +# if --solo compilation was requested, save that in zconf.h and remove gz stuff from object lists +if test $solo -eq 1; then + sed '/#define ZCONF_H/a\ +#define Z_SOLO + +' < zconf.h > zconf.temp.h + mv zconf.temp.h zconf.h +OBJC='$(OBJZ)' +PIC_OBJC='$(PIC_OBJZ)' +fi + +# if code coverage testing was requested, use older gcc if defined, e.g. "gcc-4.2" on Mac OS X +if test $cover -eq 1; then + CFLAGS="${CFLAGS} -fprofile-arcs -ftest-coverage" + if test -n "$GCC_CLASSIC"; then + CC=$GCC_CLASSIC + fi +fi + +echo >> configure.log + +# conduct a series of tests to resolve eight possible cases of using "vs" or "s" printf functions +# (using stdarg or not), with or without "n" (proving size of buffer), and with or without a +# return value. The most secure result is vsnprintf() with a return value. snprintf() with a +# return value is secure as well, but then gzprintf() will be limited to 20 arguments. +cat > $test.c < +#include +#include "zconf.h" +int main() +{ +#ifndef STDC + choke me +#endif + return 0; +} +EOF +if try $CC -c $CFLAGS $test.c; then + echo "Checking whether to use vs[n]printf() or s[n]printf()... using vs[n]printf()." | tee -a configure.log + + echo >> configure.log + cat > $test.c < +#include +int mytest(const char *fmt, ...) +{ + char buf[20]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + return 0; +} +int main() +{ + return (mytest("Hello%d\n", 1)); +} +EOF + if try $CC $CFLAGS -o $test $test.c; then + echo "Checking for vsnprintf() in stdio.h... Yes." | tee -a configure.log + + echo >> configure.log + cat >$test.c < +#include +int mytest(const char *fmt, ...) +{ + int n; + char buf[20]; + va_list ap; + va_start(ap, fmt); + n = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + return n; +} +int main() +{ + return (mytest("Hello%d\n", 1)); +} +EOF + + if try $CC -c $CFLAGS $test.c; then + echo "Checking for return value of vsnprintf()... Yes." | tee -a configure.log + else + CFLAGS="$CFLAGS -DHAS_vsnprintf_void" + SFLAGS="$SFLAGS -DHAS_vsnprintf_void" + echo "Checking for return value of vsnprintf()... No." | tee -a configure.log + echo " WARNING: apparently vsnprintf() does not return a value. zlib" | tee -a configure.log + echo " can build but will be open to possible string-format security" | tee -a configure.log + echo " vulnerabilities." | tee -a configure.log + fi + else + CFLAGS="$CFLAGS -DNO_vsnprintf" + SFLAGS="$SFLAGS -DNO_vsnprintf" + echo "Checking for vsnprintf() in stdio.h... No." | tee -a configure.log + echo " WARNING: vsnprintf() not found, falling back to vsprintf(). zlib" | tee -a configure.log + echo " can build but will be open to possible buffer-overflow security" | tee -a configure.log + echo " vulnerabilities." | tee -a configure.log + + echo >> configure.log + cat >$test.c < +#include +int mytest(const char *fmt, ...) +{ + int n; + char buf[20]; + va_list ap; + va_start(ap, fmt); + n = vsprintf(buf, fmt, ap); + va_end(ap); + return n; +} +int main() +{ + return (mytest("Hello%d\n", 1)); +} +EOF + + if try $CC -c $CFLAGS $test.c; then + echo "Checking for return value of vsprintf()... Yes." | tee -a configure.log + else + CFLAGS="$CFLAGS -DHAS_vsprintf_void" + SFLAGS="$SFLAGS -DHAS_vsprintf_void" + echo "Checking for return value of vsprintf()... No." | tee -a configure.log + echo " WARNING: apparently vsprintf() does not return a value. zlib" | tee -a configure.log + echo " can build but will be open to possible string-format security" | tee -a configure.log + echo " vulnerabilities." | tee -a configure.log + fi + fi +else + echo "Checking whether to use vs[n]printf() or s[n]printf()... using s[n]printf()." | tee -a configure.log + + echo >> configure.log + cat >$test.c < +int mytest() +{ + char buf[20]; + snprintf(buf, sizeof(buf), "%s", "foo"); + return 0; +} +int main() +{ + return (mytest()); +} +EOF + + if try $CC $CFLAGS -o $test $test.c; then + echo "Checking for snprintf() in stdio.h... Yes." | tee -a configure.log + + echo >> configure.log + cat >$test.c < +int mytest() +{ + char buf[20]; + return snprintf(buf, sizeof(buf), "%s", "foo"); +} +int main() +{ + return (mytest()); +} +EOF + + if try $CC -c $CFLAGS $test.c; then + echo "Checking for return value of snprintf()... Yes." | tee -a configure.log + else + CFLAGS="$CFLAGS -DHAS_snprintf_void" + SFLAGS="$SFLAGS -DHAS_snprintf_void" + echo "Checking for return value of snprintf()... No." | tee -a configure.log + echo " WARNING: apparently snprintf() does not return a value. zlib" | tee -a configure.log + echo " can build but will be open to possible string-format security" | tee -a configure.log + echo " vulnerabilities." | tee -a configure.log + fi + else + CFLAGS="$CFLAGS -DNO_snprintf" + SFLAGS="$SFLAGS -DNO_snprintf" + echo "Checking for snprintf() in stdio.h... No." | tee -a configure.log + echo " WARNING: snprintf() not found, falling back to sprintf(). zlib" | tee -a configure.log + echo " can build but will be open to possible buffer-overflow security" | tee -a configure.log + echo " vulnerabilities." | tee -a configure.log + + echo >> configure.log + cat >$test.c < +int mytest() +{ + char buf[20]; + return sprintf(buf, "%s", "foo"); +} +int main() +{ + return (mytest()); +} +EOF + + if try $CC -c $CFLAGS $test.c; then + echo "Checking for return value of sprintf()... Yes." | tee -a configure.log + else + CFLAGS="$CFLAGS -DHAS_sprintf_void" + SFLAGS="$SFLAGS -DHAS_sprintf_void" + echo "Checking for return value of sprintf()... No." | tee -a configure.log + echo " WARNING: apparently sprintf() does not return a value. zlib" | tee -a configure.log + echo " can build but will be open to possible string-format security" | tee -a configure.log + echo " vulnerabilities." | tee -a configure.log + fi + fi +fi + +# see if we can hide zlib internal symbols that are linked between separate source files +if test "$gcc" -eq 1; then + echo >> configure.log + cat > $test.c <> configure.log +echo ALL = $ALL >> configure.log +echo AR = $AR >> configure.log +echo ARFLAGS = $ARFLAGS >> configure.log +echo CC = $CC >> configure.log +echo CFLAGS = $CFLAGS >> configure.log +echo CPP = $CPP >> configure.log +echo EXE = $EXE >> configure.log +echo LDCONFIG = $LDCONFIG >> configure.log +echo LDFLAGS = $LDFLAGS >> configure.log +echo LDSHARED = $LDSHARED >> configure.log +echo LDSHAREDLIBC = $LDSHAREDLIBC >> configure.log +echo OBJC = $OBJC >> configure.log +echo PIC_OBJC = $PIC_OBJC >> configure.log +echo RANLIB = $RANLIB >> configure.log +echo SFLAGS = $SFLAGS >> configure.log +echo SHAREDLIB = $SHAREDLIB >> configure.log +echo SHAREDLIBM = $SHAREDLIBM >> configure.log +echo SHAREDLIBV = $SHAREDLIBV >> configure.log +echo STATICLIB = $STATICLIB >> configure.log +echo TEST = $TEST >> configure.log +echo VER = $VER >> configure.log +echo Z_U4 = $Z_U4 >> configure.log +echo SRCDIR = $SRCDIR >> configure.log +echo exec_prefix = $exec_prefix >> configure.log +echo includedir = $includedir >> configure.log +echo libdir = $libdir >> configure.log +echo mandir = $mandir >> configure.log +echo prefix = $prefix >> configure.log +echo sharedlibdir = $sharedlibdir >> configure.log +echo uname = $uname >> configure.log + +# udpate Makefile with the configure results +sed < ${SRCDIR}Makefile.in " +/^CC *=/s#=.*#=$CC# +/^CFLAGS *=/s#=.*#=$CFLAGS# +/^SFLAGS *=/s#=.*#=$SFLAGS# +/^LDFLAGS *=/s#=.*#=$LDFLAGS# +/^LDSHARED *=/s#=.*#=$LDSHARED# +/^CPP *=/s#=.*#=$CPP# +/^STATICLIB *=/s#=.*#=$STATICLIB# +/^SHAREDLIB *=/s#=.*#=$SHAREDLIB# +/^SHAREDLIBV *=/s#=.*#=$SHAREDLIBV# +/^SHAREDLIBM *=/s#=.*#=$SHAREDLIBM# +/^AR *=/s#=.*#=$AR# +/^ARFLAGS *=/s#=.*#=$ARFLAGS# +/^RANLIB *=/s#=.*#=$RANLIB# +/^LDCONFIG *=/s#=.*#=$LDCONFIG# +/^LDSHAREDLIBC *=/s#=.*#=$LDSHAREDLIBC# +/^EXE *=/s#=.*#=$EXE# +/^SRCDIR *=/s#=.*#=$SRCDIR# +/^ZINC *=/s#=.*#=$ZINC# +/^ZINCOUT *=/s#=.*#=$ZINCOUT# +/^prefix *=/s#=.*#=$prefix# +/^exec_prefix *=/s#=.*#=$exec_prefix# +/^libdir *=/s#=.*#=$libdir# +/^sharedlibdir *=/s#=.*#=$sharedlibdir# +/^includedir *=/s#=.*#=$includedir# +/^mandir *=/s#=.*#=$mandir# +/^OBJC *=/s#=.*#= $OBJC# +/^PIC_OBJC *=/s#=.*#= $PIC_OBJC# +/^all: */s#:.*#: $ALL# +/^test: */s#:.*#: $TEST# +" > Makefile + +# create zlib.pc with the configure results +sed < ${SRCDIR}zlib.pc.in " +/^CC *=/s#=.*#=$CC# +/^CFLAGS *=/s#=.*#=$CFLAGS# +/^CPP *=/s#=.*#=$CPP# +/^LDSHARED *=/s#=.*#=$LDSHARED# +/^STATICLIB *=/s#=.*#=$STATICLIB# +/^SHAREDLIB *=/s#=.*#=$SHAREDLIB# +/^SHAREDLIBV *=/s#=.*#=$SHAREDLIBV# +/^SHAREDLIBM *=/s#=.*#=$SHAREDLIBM# +/^AR *=/s#=.*#=$AR# +/^ARFLAGS *=/s#=.*#=$ARFLAGS# +/^RANLIB *=/s#=.*#=$RANLIB# +/^EXE *=/s#=.*#=$EXE# +/^prefix *=/s#=.*#=$prefix# +/^exec_prefix *=/s#=.*#=$exec_prefix# +/^libdir *=/s#=.*#=$libdir# +/^sharedlibdir *=/s#=.*#=$sharedlibdir# +/^includedir *=/s#=.*#=$includedir# +/^mandir *=/s#=.*#=$mandir# +/^LDFLAGS *=/s#=.*#=$LDFLAGS# +" | sed -e " +s/\@VERSION\@/$VER/g; +" > zlib.pc + +# done +leave 0 diff --git a/contrib/zlib/crc32.c b/contrib/zlib/crc32.c index 979a7190a..e72636a50 100644 --- a/contrib/zlib/crc32.c +++ b/contrib/zlib/crc32.c @@ -1,5 +1,5 @@ /* crc32.c -- compute the CRC-32 of a data stream - * Copyright (C) 1995-2006, 2010, 2011, 2012 Mark Adler + * Copyright (C) 1995-2006, 2010, 2011, 2012, 2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h * * Thanks to Rodney Brown for his contribution of faster @@ -30,17 +30,15 @@ #include "zutil.h" /* for STDC and FAR definitions */ -#define local static - /* Definitions for doing the crc four data bytes at a time. */ #if !defined(NOBYFOUR) && defined(Z_U4) # define BYFOUR #endif #ifdef BYFOUR local unsigned long crc32_little OF((unsigned long, - const unsigned char FAR *, unsigned)); + const unsigned char FAR *, z_size_t)); local unsigned long crc32_big OF((unsigned long, - const unsigned char FAR *, unsigned)); + const unsigned char FAR *, z_size_t)); # define TBLS 8 #else # define TBLS 1 @@ -201,10 +199,10 @@ const z_crc_t FAR * ZEXPORT get_crc_table() #define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 /* ========================================================================= */ -unsigned long ZEXPORT crc32(crc, buf, len) +unsigned long ZEXPORT crc32_z(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; - uInt len; + z_size_t len; { if (buf == Z_NULL) return 0UL; @@ -214,7 +212,7 @@ unsigned long ZEXPORT crc32(crc, buf, len) #endif /* DYNAMIC_CRC_TABLE */ #ifdef BYFOUR - if (sizeof(void *) == sizeof(ptrdiff_t)) { + if (sizeof(void *) == sizeof(z_size_t)) { z_crc_t endian; endian = 1; @@ -235,8 +233,29 @@ unsigned long ZEXPORT crc32(crc, buf, len) return crc ^ 0xffffffffUL; } +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + uInt len; +{ + return crc32_z(crc, buf, len); +} + #ifdef BYFOUR +/* + This BYFOUR code accesses the passed unsigned char * buffer with a 32-bit + integer pointer type. This violates the strict aliasing rule, where a + compiler can assume, for optimization purposes, that two pointers to + fundamentally different types won't ever point to the same memory. This can + manifest as a problem only if one of the pointers is written to. This code + only reads from those pointers. So long as this code remains isolated in + this compilation unit, there won't be a problem. For this reason, this code + should not be copied and pasted into a compilation unit in which other code + writes to the buffer that is passed to these routines. + */ + /* ========================================================================= */ #define DOLIT4 c ^= *buf4++; \ c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ @@ -247,14 +266,14 @@ unsigned long ZEXPORT crc32(crc, buf, len) local unsigned long crc32_little(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; - unsigned len; + z_size_t len; { register z_crc_t c; register const z_crc_t FAR *buf4; c = (z_crc_t)crc; c = ~c; - while (len && ((ptrdiff_t)buf & 3)) { + while (len && ((z_size_t)buf & 3)) { c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); len--; } @@ -278,7 +297,7 @@ local unsigned long crc32_little(crc, buf, len) } /* ========================================================================= */ -#define DOBIG4 c ^= *++buf4; \ +#define DOBIG4 c ^= *buf4++; \ c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] #define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 @@ -287,20 +306,19 @@ local unsigned long crc32_little(crc, buf, len) local unsigned long crc32_big(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; - unsigned len; + z_size_t len; { register z_crc_t c; register const z_crc_t FAR *buf4; c = ZSWAP32((z_crc_t)crc); c = ~c; - while (len && ((ptrdiff_t)buf & 3)) { + while (len && ((z_size_t)buf & 3)) { c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); len--; } buf4 = (const z_crc_t FAR *)(const void FAR *)buf; - buf4--; while (len >= 32) { DOBIG32; len -= 32; @@ -309,7 +327,6 @@ local unsigned long crc32_big(crc, buf, len) DOBIG4; len -= 4; } - buf4++; buf = (const unsigned char FAR *)buf4; if (len) do { diff --git a/contrib/zlib/deflate.c b/contrib/zlib/deflate.c index 696957705..568eaddbe 100644 --- a/contrib/zlib/deflate.c +++ b/contrib/zlib/deflate.c @@ -1,5 +1,5 @@ /* deflate.c -- compress data using the deflation algorithm - * Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler + * Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -52,7 +52,7 @@ #include "deflate.h" const char deflate_copyright[] = - " deflate 1.2.8 Copyright 1995-2013 Jean-loup Gailly and Mark Adler "; + " deflate 1.2.11.1 Copyright 1995-2017 Jean-loup Gailly and Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -73,6 +73,8 @@ typedef enum { typedef block_state (*compress_func) OF((deflate_state *s, int flush)); /* Compression function. Returns the block state after the call. */ +local int deflateStateCheck OF((z_streamp strm)); +local void slide_hash OF((deflate_state *s)); local void fill_window OF((deflate_state *s)); local block_state deflate_stored OF((deflate_state *s, int flush)); local block_state deflate_fast OF((deflate_state *s, int flush)); @@ -84,15 +86,16 @@ local block_state deflate_huff OF((deflate_state *s, int flush)); local void lm_init OF((deflate_state *s)); local void putShortMSB OF((deflate_state *s, uInt b)); local void flush_pending OF((z_streamp strm)); -local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +local unsigned read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); #ifdef ASMV +# pragma message("Assembler code may have bugs -- use at your own risk") void match_init OF((void)); /* asm code initialization */ uInt longest_match OF((deflate_state *s, IPos cur_match)); #else local uInt longest_match OF((deflate_state *s, IPos cur_match)); #endif -#ifdef DEBUG +#ifdef ZLIB_DEBUG local void check_match OF((deflate_state *s, IPos start, IPos match, int length)); #endif @@ -148,21 +151,14 @@ local const config configuration_table[10] = { * meaning. */ -#define EQUAL 0 -/* result of memcmp for equal strings */ - -#ifndef NO_DUMMY_DECL -struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ -#endif - /* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */ -#define RANK(f) (((f) << 1) - ((f) > 4 ? 9 : 0)) +#define RANK(f) (((f) * 2) - ((f) > 4 ? 9 : 0)) /* =========================================================================== * Update a hash value with the given input byte - * IN assertion: all calls to to UPDATE_HASH are made with consecutive - * input characters, so that a running hash key can be computed from the - * previous key instead of complete recalculation each time. + * IN assertion: all calls to UPDATE_HASH are made with consecutive input + * characters, so that a running hash key can be computed from the previous + * key instead of complete recalculation each time. */ #define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) @@ -173,9 +169,9 @@ struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ * the previous length of the hash chain. * If this file is compiled with -DFASTEST, the compression level is forced * to 1, and no hash chains are maintained. - * IN assertion: all calls to to INSERT_STRING are made with consecutive - * input characters and the first MIN_MATCH bytes of str are valid - * (except for the last MIN_MATCH-1 bytes of the input file). + * IN assertion: all calls to INSERT_STRING are made with consecutive input + * characters and the first MIN_MATCH bytes of str are valid (except for + * the last MIN_MATCH-1 bytes of the input file). */ #ifdef FASTEST #define INSERT_STRING(s, str, match_head) \ @@ -194,8 +190,42 @@ struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ * prev[] will be initialized on the fly. */ #define CLEAR_HASH(s) \ - s->head[s->hash_size-1] = NIL; \ - zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + do { \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, \ + (unsigned)(s->hash_size-1)*sizeof(*s->head)); \ + } while (0) + +/* =========================================================================== + * Slide the hash table when sliding the window down (could be avoided with 32 + * bit values at the expense of memory usage). We slide even when level == 0 to + * keep the hash table consistent if we switch back to level > 0 later. + */ +local void slide_hash(s) + deflate_state *s; +{ + unsigned n, m; + Posf *p; + uInt wsize = s->w_size; + + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m - wsize : NIL); + } while (--n); + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m - wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif +} /* ========================================================================= */ int ZEXPORT deflateInit_(strm, level, version, stream_size) @@ -270,7 +300,7 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, #endif if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || - strategy < 0 || strategy > Z_FIXED) { + strategy < 0 || strategy > Z_FIXED || (windowBits == 8 && wrap != 1)) { return Z_STREAM_ERROR; } if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ @@ -278,14 +308,15 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, if (s == Z_NULL) return Z_MEM_ERROR; strm->state = (struct internal_state FAR *)s; s->strm = strm; + s->status = INIT_STATE; /* to pass state test in deflateReset() */ s->wrap = wrap; s->gzhead = Z_NULL; - s->w_bits = windowBits; + s->w_bits = (uInt)windowBits; s->w_size = 1 << s->w_bits; s->w_mask = s->w_size - 1; - s->hash_bits = memLevel + 7; + s->hash_bits = (uInt)memLevel + 7; s->hash_size = 1 << s->hash_bits; s->hash_mask = s->hash_size - 1; s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); @@ -319,6 +350,31 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, return deflateReset(strm); } +/* ========================================================================= + * Check for a valid deflate stream state. Return 0 if ok, 1 if not. + */ +local int deflateStateCheck (strm) + z_streamp strm; +{ + deflate_state *s; + if (strm == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) + return 1; + s = strm->state; + if (s == Z_NULL || s->strm != strm || (s->status != INIT_STATE && +#ifdef GZIP + s->status != GZIP_STATE && +#endif + s->status != EXTRA_STATE && + s->status != NAME_STATE && + s->status != COMMENT_STATE && + s->status != HCRC_STATE && + s->status != BUSY_STATE && + s->status != FINISH_STATE)) + return 1; + return 0; +} + /* ========================================================================= */ int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) z_streamp strm; @@ -331,7 +387,7 @@ int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) unsigned avail; z_const unsigned char *next; - if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL) + if (deflateStateCheck(strm) || dictionary == Z_NULL) return Z_STREAM_ERROR; s = strm->state; wrap = s->wrap; @@ -388,14 +444,35 @@ int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) return Z_OK; } +/* ========================================================================= */ +int ZEXPORT deflateGetDictionary (strm, dictionary, dictLength) + z_streamp strm; + Bytef *dictionary; + uInt *dictLength; +{ + deflate_state *s; + uInt len; + + if (deflateStateCheck(strm)) + return Z_STREAM_ERROR; + s = strm->state; + len = s->strstart + s->lookahead; + if (len > s->w_size) + len = s->w_size; + if (dictionary != Z_NULL && len) + zmemcpy(dictionary, s->window + s->strstart + s->lookahead - len, len); + if (dictLength != Z_NULL) + *dictLength = len; + return Z_OK; +} + /* ========================================================================= */ int ZEXPORT deflateResetKeep (strm) z_streamp strm; { deflate_state *s; - if (strm == Z_NULL || strm->state == Z_NULL || - strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + if (deflateStateCheck(strm)) { return Z_STREAM_ERROR; } @@ -410,13 +487,17 @@ int ZEXPORT deflateResetKeep (strm) if (s->wrap < 0) { s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ } - s->status = s->wrap ? INIT_STATE : BUSY_STATE; + s->status = +#ifdef GZIP + s->wrap == 2 ? GZIP_STATE : +#endif + s->wrap ? INIT_STATE : BUSY_STATE; strm->adler = #ifdef GZIP s->wrap == 2 ? crc32(0L, Z_NULL, 0) : #endif adler32(0L, Z_NULL, 0); - s->last_flush = Z_NO_FLUSH; + s->last_flush = -2; _tr_init(s); @@ -440,8 +521,8 @@ int ZEXPORT deflateSetHeader (strm, head) z_streamp strm; gz_headerp head; { - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - if (strm->state->wrap != 2) return Z_STREAM_ERROR; + if (deflateStateCheck(strm) || strm->state->wrap != 2) + return Z_STREAM_ERROR; strm->state->gzhead = head; return Z_OK; } @@ -452,7 +533,7 @@ int ZEXPORT deflatePending (strm, pending, bits) int *bits; z_streamp strm; { - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (deflateStateCheck(strm)) return Z_STREAM_ERROR; if (pending != Z_NULL) *pending = strm->state->pending; if (bits != Z_NULL) @@ -469,7 +550,7 @@ int ZEXPORT deflatePrime (strm, bits, value) deflate_state *s; int put; - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3)) return Z_BUF_ERROR; @@ -494,9 +575,8 @@ int ZEXPORT deflateParams(strm, level, strategy) { deflate_state *s; compress_func func; - int err = Z_OK; - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; #ifdef FASTEST @@ -510,13 +590,22 @@ int ZEXPORT deflateParams(strm, level, strategy) func = configuration_table[s->level].func; if ((strategy != s->strategy || func != configuration_table[level].func) && - strm->total_in != 0) { + s->last_flush != -2) { /* Flush the last buffer: */ - err = deflate(strm, Z_BLOCK); - if (err == Z_BUF_ERROR && s->pending == 0) - err = Z_OK; + int err = deflate(strm, Z_BLOCK); + if (err == Z_STREAM_ERROR) + return err; + if (strm->avail_in || (s->strstart - s->block_start) + s->lookahead) + return Z_BUF_ERROR; } if (s->level != level) { + if (s->level == 0 && s->matches != 0) { + if (s->matches == 1) + slide_hash(s); + else + CLEAR_HASH(s); + s->matches = 0; + } s->level = level; s->max_lazy_match = configuration_table[level].max_lazy; s->good_match = configuration_table[level].good_length; @@ -524,7 +613,7 @@ int ZEXPORT deflateParams(strm, level, strategy) s->max_chain_length = configuration_table[level].max_chain; } s->strategy = strategy; - return err; + return Z_OK; } /* ========================================================================= */ @@ -537,12 +626,12 @@ int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) { deflate_state *s; - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; - s->good_match = good_length; - s->max_lazy_match = max_lazy; + s->good_match = (uInt)good_length; + s->max_lazy_match = (uInt)max_lazy; s->nice_match = nice_length; - s->max_chain_length = max_chain; + s->max_chain_length = (uInt)max_chain; return Z_OK; } @@ -569,14 +658,13 @@ uLong ZEXPORT deflateBound(strm, sourceLen) { deflate_state *s; uLong complen, wraplen; - Bytef *str; /* conservative upper bound for compressed data */ complen = sourceLen + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; /* if can't get parameters, return conservative bound plus zlib wrapper */ - if (strm == Z_NULL || strm->state == Z_NULL) + if (deflateStateCheck(strm)) return complen + 6; /* compute wrapper length */ @@ -588,9 +676,11 @@ uLong ZEXPORT deflateBound(strm, sourceLen) case 1: /* zlib wrapper */ wraplen = 6 + (s->strstart ? 4 : 0); break; +#ifdef GZIP case 2: /* gzip wrapper */ wraplen = 18; if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ + Bytef *str; if (s->gzhead->extra != Z_NULL) wraplen += 2 + s->gzhead->extra_len; str = s->gzhead->name; @@ -607,6 +697,7 @@ uLong ZEXPORT deflateBound(strm, sourceLen) wraplen += 2; } break; +#endif default: /* for compiler happiness */ wraplen = 6; } @@ -634,10 +725,10 @@ local void putShortMSB (s, b) } /* ========================================================================= - * Flush as much pending output as possible. All deflate() output goes - * through this function so some applications may wish to modify it - * to avoid allocating a large strm->next_out buffer and copying into it. - * (See also read_buf()). + * Flush as much pending output as possible. All deflate() output, except for + * some deflate_stored() output, goes through this function so some + * applications may wish to modify it to avoid allocating a large + * strm->next_out buffer and copying into it. (See also read_buf()). */ local void flush_pending(strm) z_streamp strm; @@ -654,13 +745,23 @@ local void flush_pending(strm) strm->next_out += len; s->pending_out += len; strm->total_out += len; - strm->avail_out -= len; - s->pending -= len; + strm->avail_out -= len; + s->pending -= len; if (s->pending == 0) { s->pending_out = s->pending_buf; } } +/* =========================================================================== + * Update the header CRC with the bytes s->pending_buf[beg..s->pending - 1]. + */ +#define HCRC_UPDATE(beg) \ + do { \ + if (s->gzhead->hcrc && s->pending > (beg)) \ + strm->adler = crc32(strm->adler, s->pending_buf + (beg), \ + s->pending - (beg)); \ + } while (0) + /* ========================================================================= */ int ZEXPORT deflate (strm, flush) z_streamp strm; @@ -669,203 +770,21 @@ int ZEXPORT deflate (strm, flush) int old_flush; /* value of flush param for previous deflate call */ deflate_state *s; - if (strm == Z_NULL || strm->state == Z_NULL || - flush > Z_BLOCK || flush < 0) { + if (deflateStateCheck(strm) || flush > Z_BLOCK || flush < 0) { return Z_STREAM_ERROR; } s = strm->state; if (strm->next_out == Z_NULL || - (strm->next_in == Z_NULL && strm->avail_in != 0) || + (strm->avail_in != 0 && strm->next_in == Z_NULL) || (s->status == FINISH_STATE && flush != Z_FINISH)) { ERR_RETURN(strm, Z_STREAM_ERROR); } if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); - s->strm = strm; /* just in case */ old_flush = s->last_flush; s->last_flush = flush; - /* Write the header */ - if (s->status == INIT_STATE) { -#ifdef GZIP - if (s->wrap == 2) { - strm->adler = crc32(0L, Z_NULL, 0); - put_byte(s, 31); - put_byte(s, 139); - put_byte(s, 8); - if (s->gzhead == Z_NULL) { - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, s->level == 9 ? 2 : - (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? - 4 : 0)); - put_byte(s, OS_CODE); - s->status = BUSY_STATE; - } - else { - put_byte(s, (s->gzhead->text ? 1 : 0) + - (s->gzhead->hcrc ? 2 : 0) + - (s->gzhead->extra == Z_NULL ? 0 : 4) + - (s->gzhead->name == Z_NULL ? 0 : 8) + - (s->gzhead->comment == Z_NULL ? 0 : 16) - ); - put_byte(s, (Byte)(s->gzhead->time & 0xff)); - put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); - put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); - put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); - put_byte(s, s->level == 9 ? 2 : - (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? - 4 : 0)); - put_byte(s, s->gzhead->os & 0xff); - if (s->gzhead->extra != Z_NULL) { - put_byte(s, s->gzhead->extra_len & 0xff); - put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); - } - if (s->gzhead->hcrc) - strm->adler = crc32(strm->adler, s->pending_buf, - s->pending); - s->gzindex = 0; - s->status = EXTRA_STATE; - } - } - else -#endif - { - uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; - uInt level_flags; - - if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) - level_flags = 0; - else if (s->level < 6) - level_flags = 1; - else if (s->level == 6) - level_flags = 2; - else - level_flags = 3; - header |= (level_flags << 6); - if (s->strstart != 0) header |= PRESET_DICT; - header += 31 - (header % 31); - - s->status = BUSY_STATE; - putShortMSB(s, header); - - /* Save the adler32 of the preset dictionary: */ - if (s->strstart != 0) { - putShortMSB(s, (uInt)(strm->adler >> 16)); - putShortMSB(s, (uInt)(strm->adler & 0xffff)); - } - strm->adler = adler32(0L, Z_NULL, 0); - } - } -#ifdef GZIP - if (s->status == EXTRA_STATE) { - if (s->gzhead->extra != Z_NULL) { - uInt beg = s->pending; /* start of bytes to update crc */ - - while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { - if (s->pending == s->pending_buf_size) { - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - flush_pending(strm); - beg = s->pending; - if (s->pending == s->pending_buf_size) - break; - } - put_byte(s, s->gzhead->extra[s->gzindex]); - s->gzindex++; - } - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - if (s->gzindex == s->gzhead->extra_len) { - s->gzindex = 0; - s->status = NAME_STATE; - } - } - else - s->status = NAME_STATE; - } - if (s->status == NAME_STATE) { - if (s->gzhead->name != Z_NULL) { - uInt beg = s->pending; /* start of bytes to update crc */ - int val; - - do { - if (s->pending == s->pending_buf_size) { - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - flush_pending(strm); - beg = s->pending; - if (s->pending == s->pending_buf_size) { - val = 1; - break; - } - } - val = s->gzhead->name[s->gzindex++]; - put_byte(s, val); - } while (val != 0); - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - if (val == 0) { - s->gzindex = 0; - s->status = COMMENT_STATE; - } - } - else - s->status = COMMENT_STATE; - } - if (s->status == COMMENT_STATE) { - if (s->gzhead->comment != Z_NULL) { - uInt beg = s->pending; /* start of bytes to update crc */ - int val; - - do { - if (s->pending == s->pending_buf_size) { - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - flush_pending(strm); - beg = s->pending; - if (s->pending == s->pending_buf_size) { - val = 1; - break; - } - } - val = s->gzhead->comment[s->gzindex++]; - put_byte(s, val); - } while (val != 0); - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - if (val == 0) - s->status = HCRC_STATE; - } - else - s->status = HCRC_STATE; - } - if (s->status == HCRC_STATE) { - if (s->gzhead->hcrc) { - if (s->pending + 2 > s->pending_buf_size) - flush_pending(strm); - if (s->pending + 2 <= s->pending_buf_size) { - put_byte(s, (Byte)(strm->adler & 0xff)); - put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); - strm->adler = crc32(0L, Z_NULL, 0); - s->status = BUSY_STATE; - } - } - else - s->status = BUSY_STATE; - } -#endif - /* Flush as much pending output as possible */ if (s->pending != 0) { flush_pending(strm); @@ -894,15 +813,197 @@ int ZEXPORT deflate (strm, flush) ERR_RETURN(strm, Z_BUF_ERROR); } + /* Write the header */ + if (s->status == INIT_STATE) { + /* zlib header */ + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + + /* Compression must start with an empty pending buffer */ + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } + } +#ifdef GZIP + if (s->status == GZIP_STATE) { + /* gzip header */ + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == Z_NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + + /* Compression must start with an empty pending buffer */ + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != Z_NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != Z_NULL) { + ulg beg = s->pending; /* start of bytes to update crc */ + uInt left = (s->gzhead->extra_len & 0xffff) - s->gzindex; + while (s->pending + left > s->pending_buf_size) { + uInt copy = s->pending_buf_size - s->pending; + zmemcpy(s->pending_buf + s->pending, + s->gzhead->extra + s->gzindex, copy); + s->pending = s->pending_buf_size; + HCRC_UPDATE(beg); + s->gzindex += copy; + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } + beg = 0; + left -= copy; + } + zmemcpy(s->pending_buf + s->pending, + s->gzhead->extra + s->gzindex, left); + s->pending += left; + HCRC_UPDATE(beg); + s->gzindex = 0; + } + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != Z_NULL) { + ulg beg = s->pending; /* start of bytes to update crc */ + int val; + do { + if (s->pending == s->pending_buf_size) { + HCRC_UPDATE(beg); + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } + beg = 0; + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + HCRC_UPDATE(beg); + s->gzindex = 0; + } + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != Z_NULL) { + ulg beg = s->pending; /* start of bytes to update crc */ + int val; + do { + if (s->pending == s->pending_buf_size) { + HCRC_UPDATE(beg); + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } + beg = 0; + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + HCRC_UPDATE(beg); + } + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) { + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } + } + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + } + s->status = BUSY_STATE; + + /* Compression must start with an empty pending buffer */ + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } + } +#endif + /* Start a new block or continue the current one. */ if (strm->avail_in != 0 || s->lookahead != 0 || (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { block_state bstate; - bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : - (s->strategy == Z_RLE ? deflate_rle(s, flush) : - (*(configuration_table[s->level].func))(s, flush)); + bstate = s->level == 0 ? deflate_stored(s, flush) : + s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : + s->strategy == Z_RLE ? deflate_rle(s, flush) : + (*(configuration_table[s->level].func))(s, flush); if (bstate == finish_started || bstate == finish_done) { s->status = FINISH_STATE; @@ -944,7 +1045,6 @@ int ZEXPORT deflate (strm, flush) } } } - Assert(strm->avail_out > 0, "bug2"); if (flush != Z_FINISH) return Z_OK; if (s->wrap <= 0) return Z_STREAM_END; @@ -981,18 +1081,9 @@ int ZEXPORT deflateEnd (strm) { int status; - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (deflateStateCheck(strm)) return Z_STREAM_ERROR; status = strm->state->status; - if (status != INIT_STATE && - status != EXTRA_STATE && - status != NAME_STATE && - status != COMMENT_STATE && - status != HCRC_STATE && - status != BUSY_STATE && - status != FINISH_STATE) { - return Z_STREAM_ERROR; - } /* Deallocate in reverse order of allocations: */ TRY_FREE(strm, strm->state->pending_buf); @@ -1023,7 +1114,7 @@ int ZEXPORT deflateCopy (dest, source) ushf *overlay; - if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + if (deflateStateCheck(source) || dest == Z_NULL) { return Z_STREAM_ERROR; } @@ -1073,7 +1164,7 @@ int ZEXPORT deflateCopy (dest, source) * allocating a large strm->next_in buffer and copying from it. * (See also flush_pending()). */ -local int read_buf(strm, buf, size) +local unsigned read_buf(strm, buf, size) z_streamp strm; Bytef *buf; unsigned size; @@ -1097,7 +1188,7 @@ local int read_buf(strm, buf, size) strm->next_in += len; strm->total_in += len; - return (int)len; + return len; } /* =========================================================================== @@ -1151,9 +1242,9 @@ local uInt longest_match(s, cur_match) { unsigned chain_length = s->max_chain_length;/* max hash chain length */ register Bytef *scan = s->window + s->strstart; /* current string */ - register Bytef *match; /* matched string */ + register Bytef *match; /* matched string */ register int len; /* length of current match */ - int best_len = s->prev_length; /* best match length so far */ + int best_len = (int)s->prev_length; /* best match length so far */ int nice_match = s->nice_match; /* stop if match long enough */ IPos limit = s->strstart > (IPos)MAX_DIST(s) ? s->strstart - (IPos)MAX_DIST(s) : NIL; @@ -1188,7 +1279,7 @@ local uInt longest_match(s, cur_match) /* Do not look for matches beyond the end of the input. This is necessary * to make deflate deterministic. */ - if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + if ((uInt)nice_match > s->lookahead) nice_match = (int)s->lookahead; Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); @@ -1349,7 +1440,11 @@ local uInt longest_match(s, cur_match) #endif /* FASTEST */ -#ifdef DEBUG +#ifdef ZLIB_DEBUG + +#define EQUAL 0 +/* result of memcmp for equal strings */ + /* =========================================================================== * Check that the match at match_start is indeed a match. */ @@ -1375,7 +1470,7 @@ local void check_match(s, start, match, length) } #else # define check_match(s, start, match, length) -#endif /* DEBUG */ +#endif /* ZLIB_DEBUG */ /* =========================================================================== * Fill the window when the lookahead becomes insufficient. @@ -1390,8 +1485,7 @@ local void check_match(s, start, match, length) local void fill_window(s) deflate_state *s; { - register unsigned n, m; - register Posf *p; + unsigned n; unsigned more; /* Amount of free space at the end of the window. */ uInt wsize = s->w_size; @@ -1418,35 +1512,13 @@ local void fill_window(s) */ if (s->strstart >= wsize+MAX_DIST(s)) { - zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + zmemcpy(s->window, s->window+wsize, (unsigned)wsize - more); s->match_start -= wsize; s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ s->block_start -= (long) wsize; - - /* Slide the hash table (could be avoided with 32 bit values - at the expense of memory usage). We slide even when level == 0 - to keep the hash table consistent if we switch back to level > 0 - later. (Using level 0 permanently is not an optimal usage of - zlib, so we don't care about this pathological case.) - */ - n = s->hash_size; - p = &s->head[n]; - do { - m = *--p; - *p = (Pos)(m >= wsize ? m-wsize : NIL); - } while (--n); - - n = wsize; -#ifndef FASTEST - p = &s->prev[n]; - do { - m = *--p; - *p = (Pos)(m >= wsize ? m-wsize : NIL); - /* If n is not on any hash chain, prev[n] is garbage but - * its value will never be used. - */ - } while (--n); -#endif + if (s->insert > s->strstart) + s->insert = s->strstart; + slide_hash(s); more += wsize; } if (s->strm->avail_in == 0) break; @@ -1552,70 +1624,205 @@ local void fill_window(s) if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ } +/* Maximum stored block length in deflate format (not including header). */ +#define MAX_STORED 65535 + +/* Minimum of a and b. */ +#define MIN(a, b) ((a) > (b) ? (b) : (a)) + /* =========================================================================== * Copy without compression as much as possible from the input stream, return * the current block state. - * This function does not insert new strings in the dictionary since - * uncompressible data is probably not useful. This function is used - * only for the level=0 compression option. - * NOTE: this function should be optimized to avoid extra copying from - * window to pending_buf. + * + * In case deflateParams() is used to later switch to a non-zero compression + * level, s->matches (otherwise unused when storing) keeps track of the number + * of hash table slides to perform. If s->matches is 1, then one hash table + * slide will be done when switching. If s->matches is 2, the maximum value + * allowed here, then the hash table will be cleared, since two or more slides + * is the same as a clear. + * + * deflate_stored() is written to minimize the number of times an input byte is + * copied. It is most efficient with large input and output buffers, which + * maximizes the opportunites to have a single copy from next_in to next_out. */ local block_state deflate_stored(s, flush) deflate_state *s; int flush; { - /* Stored blocks are limited to 0xffff bytes, pending_buf is limited - * to pending_buf_size, and each stored block has a 5 byte header: + /* Smallest worthy block size when not flushing or finishing. By default + * this is 32K. This can be as small as 507 bytes for memLevel == 1. For + * large input and output buffers, the stored block size will be larger. */ - ulg max_block_size = 0xffff; - ulg max_start; + unsigned min_block = MIN(s->pending_buf_size - 5, s->w_size); - if (max_block_size > s->pending_buf_size - 5) { - max_block_size = s->pending_buf_size - 5; - } - - /* Copy as much as possible from input to output: */ - for (;;) { - /* Fill the window as much as possible: */ - if (s->lookahead <= 1) { - - Assert(s->strstart < s->w_size+MAX_DIST(s) || - s->block_start >= (long)s->w_size, "slide too late"); - - fill_window(s); - if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; - - if (s->lookahead == 0) break; /* flush the current block */ - } - Assert(s->block_start >= 0L, "block gone"); - - s->strstart += s->lookahead; - s->lookahead = 0; - - /* Emit a stored block if pending_buf will be full: */ - max_start = s->block_start + max_block_size; - if (s->strstart == 0 || (ulg)s->strstart >= max_start) { - /* strstart == 0 is possible when wraparound on 16-bit machine */ - s->lookahead = (uInt)(s->strstart - max_start); - s->strstart = (uInt)max_start; - FLUSH_BLOCK(s, 0); - } - /* Flush if we may have to slide, otherwise block_start may become - * negative and the data will be gone: + /* Copy as many min_block or larger stored blocks directly to next_out as + * possible. If flushing, copy the remaining available input to next_out as + * stored blocks, if there is enough space. + */ + unsigned len, left, have, last = 0; + unsigned used = s->strm->avail_in; + do { + /* Set len to the maximum size block that we can copy directly with the + * available input data and output space. Set left to how much of that + * would be copied from what's left in the window. */ - if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { - FLUSH_BLOCK(s, 0); + len = MAX_STORED; /* maximum deflate stored block length */ + have = (s->bi_valid + 42) >> 3; /* number of header bytes */ + if (s->strm->avail_out < have) /* need room for header */ + break; + /* maximum stored block length that will fit in avail_out: */ + have = s->strm->avail_out - have; + left = s->strstart - s->block_start; /* bytes left in window */ + if (len > (ulg)left + s->strm->avail_in) + len = left + s->strm->avail_in; /* limit len to the input */ + if (len > have) + len = have; /* limit len to the output */ + + /* If the stored block would be less than min_block in length, or if + * unable to copy all of the available input when flushing, then try + * copying to the window and the pending buffer instead. Also don't + * write an empty block when flushing -- deflate() does that. + */ + if (len < min_block && ((len == 0 && flush != Z_FINISH) || + flush == Z_NO_FLUSH || + len != left + s->strm->avail_in)) + break; + + /* Make a dummy stored block in pending to get the header bytes, + * including any pending bits. This also updates the debugging counts. + */ + last = flush == Z_FINISH && len == left + s->strm->avail_in ? 1 : 0; + _tr_stored_block(s, (char *)0, 0L, last); + + /* Replace the lengths in the dummy stored block with len. */ + s->pending_buf[s->pending - 4] = len; + s->pending_buf[s->pending - 3] = len >> 8; + s->pending_buf[s->pending - 2] = ~len; + s->pending_buf[s->pending - 1] = ~len >> 8; + + /* Write the stored block header bytes. */ + flush_pending(s->strm); + +#ifdef ZLIB_DEBUG + /* Update debugging counts for the data about to be copied. */ + s->compressed_len += len << 3; + s->bits_sent += len << 3; +#endif + + /* Copy uncompressed bytes from the window to next_out. */ + if (left) { + if (left > len) + left = len; + zmemcpy(s->strm->next_out, s->window + s->block_start, left); + s->strm->next_out += left; + s->strm->avail_out -= left; + s->strm->total_out += left; + s->block_start += left; + len -= left; } + + /* Copy uncompressed bytes directly from next_in to next_out, updating + * the check value. + */ + if (len) { + read_buf(s->strm, s->strm->next_out, len); + s->strm->next_out += len; + s->strm->avail_out -= len; + s->strm->total_out += len; + } + } while (last == 0); + + /* Update the sliding window with the last s->w_size bytes of the copied + * data, or append all of the copied data to the existing window if less + * than s->w_size bytes were copied. Also update the number of bytes to + * insert in the hash tables, in the event that deflateParams() switches to + * a non-zero compression level. + */ + used -= s->strm->avail_in; /* number of input bytes directly copied */ + if (used) { + /* If any input was used, then no unused input remains in the window, + * therefore s->block_start == s->strstart. + */ + if (used >= s->w_size) { /* supplant the previous history */ + s->matches = 2; /* clear hash */ + zmemcpy(s->window, s->strm->next_in - s->w_size, s->w_size); + s->strstart = s->w_size; + s->insert = s->strstart; + } + else { + if (s->window_size - s->strstart <= used) { + /* Slide the window down. */ + s->strstart -= s->w_size; + zmemcpy(s->window, s->window + s->w_size, s->strstart); + if (s->matches < 2) + s->matches++; /* add a pending slide_hash() */ + if (s->insert > s->strstart) + s->insert = s->strstart; + } + zmemcpy(s->window + s->strstart, s->strm->next_in - used, used); + s->strstart += used; + s->insert += MIN(used, s->w_size - s->insert); + } + s->block_start = s->strstart; } - s->insert = 0; - if (flush == Z_FINISH) { - FLUSH_BLOCK(s, 1); + if (s->high_water < s->strstart) + s->high_water = s->strstart; + + /* If the last block was written to next_out, then done. */ + if (last) return finish_done; + + /* If flushing and all input has been consumed, then done. */ + if (flush != Z_NO_FLUSH && flush != Z_FINISH && + s->strm->avail_in == 0 && (long)s->strstart == s->block_start) + return block_done; + + /* Fill the window with any remaining input. */ + have = s->window_size - s->strstart; + if (s->strm->avail_in > have && s->block_start >= (long)s->w_size) { + /* Slide the window down. */ + s->block_start -= s->w_size; + s->strstart -= s->w_size; + zmemcpy(s->window, s->window + s->w_size, s->strstart); + if (s->matches < 2) + s->matches++; /* add a pending slide_hash() */ + have += s->w_size; /* more space now */ + if (s->insert > s->strstart) + s->insert = s->strstart; } - if ((long)s->strstart > s->block_start) - FLUSH_BLOCK(s, 0); - return block_done; + if (have > s->strm->avail_in) + have = s->strm->avail_in; + if (have) { + read_buf(s->strm, s->window + s->strstart, have); + s->strstart += have; + s->insert += MIN(have, s->w_size - s->insert); + } + if (s->high_water < s->strstart) + s->high_water = s->strstart; + + /* There was not enough avail_out to write a complete worthy or flushed + * stored block to next_out. Write a stored block to pending instead, if we + * have enough input for a worthy block, or if flushing and there is enough + * room for the remaining input as a stored block in the pending buffer. + */ + have = (s->bi_valid + 42) >> 3; /* number of header bytes */ + /* maximum stored block length that will fit in pending: */ + have = MIN(s->pending_buf_size - have, MAX_STORED); + min_block = MIN(have, s->w_size); + left = s->strstart - s->block_start; + if (left >= min_block || + ((left || flush == Z_FINISH) && flush != Z_NO_FLUSH && + s->strm->avail_in == 0 && left <= have)) { + len = MIN(left, have); + last = flush == Z_FINISH && s->strm->avail_in == 0 && + len == left ? 1 : 0; + _tr_stored_block(s, (charf *)s->window + s->block_start, len, last); + s->block_start += len; + flush_pending(s->strm); + } + + /* We've done all we can with the available input and output. */ + return last ? finish_started : need_more; } /* =========================================================================== @@ -1892,7 +2099,7 @@ local block_state deflate_rle(s, flush) prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && scan < strend); - s->match_length = MAX_MATCH - (int)(strend - scan); + s->match_length = MAX_MATCH - (uInt)(strend - scan); if (s->match_length > s->lookahead) s->match_length = s->lookahead; } diff --git a/contrib/zlib/deflate.h b/contrib/zlib/deflate.h index ce0299edd..23ecdd312 100644 --- a/contrib/zlib/deflate.h +++ b/contrib/zlib/deflate.h @@ -1,5 +1,5 @@ /* deflate.h -- internal compression state - * Copyright (C) 1995-2012 Jean-loup Gailly + * Copyright (C) 1995-2016 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -51,13 +51,16 @@ #define Buf_size 16 /* size of bit buffer in bi_buf */ -#define INIT_STATE 42 -#define EXTRA_STATE 69 -#define NAME_STATE 73 -#define COMMENT_STATE 91 -#define HCRC_STATE 103 -#define BUSY_STATE 113 -#define FINISH_STATE 666 +#define INIT_STATE 42 /* zlib header -> BUSY_STATE */ +#ifdef GZIP +# define GZIP_STATE 57 /* gzip header -> BUSY_STATE | EXTRA_STATE */ +#endif +#define EXTRA_STATE 69 /* gzip extra block -> NAME_STATE */ +#define NAME_STATE 73 /* gzip file name -> COMMENT_STATE */ +#define COMMENT_STATE 91 /* gzip comment -> HCRC_STATE */ +#define HCRC_STATE 103 /* gzip header CRC -> BUSY_STATE */ +#define BUSY_STATE 113 /* deflate -> FINISH_STATE */ +#define FINISH_STATE 666 /* stream complete */ /* Stream status */ @@ -83,7 +86,7 @@ typedef struct static_tree_desc_s static_tree_desc; typedef struct tree_desc_s { ct_data *dyn_tree; /* the dynamic tree */ int max_code; /* largest code with non zero frequency */ - static_tree_desc *stat_desc; /* the corresponding static tree */ + const static_tree_desc *stat_desc; /* the corresponding static tree */ } FAR tree_desc; typedef ush Pos; @@ -100,10 +103,10 @@ typedef struct internal_state { Bytef *pending_buf; /* output still pending */ ulg pending_buf_size; /* size of pending_buf */ Bytef *pending_out; /* next pending byte to output to the stream */ - uInt pending; /* nb of bytes in the pending buffer */ + ulg pending; /* nb of bytes in the pending buffer */ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ gz_headerp gzhead; /* gzip header information to write */ - uInt gzindex; /* where in extra, name, or comment */ + ulg gzindex; /* where in extra, name, or comment */ Byte method; /* can only be DEFLATED */ int last_flush; /* value of flush param for previous deflate call */ @@ -249,7 +252,7 @@ typedef struct internal_state { uInt matches; /* number of string matches in current block */ uInt insert; /* bytes at end of window left to insert */ -#ifdef DEBUG +#ifdef ZLIB_DEBUG ulg compressed_len; /* total bit length of compressed file mod 2^32 */ ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ #endif @@ -275,7 +278,7 @@ typedef struct internal_state { /* Output a byte on the stream. * IN assertion: there is enough room in pending_buf. */ -#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} +#define put_byte(s, c) {s->pending_buf[s->pending++] = (Bytef)(c);} #define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) @@ -309,7 +312,7 @@ void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, * used. */ -#ifndef DEBUG +#ifndef ZLIB_DEBUG /* Inline versions of _tr_tally for speed: */ #if defined(GEN_TREES_H) || !defined(STDC) @@ -328,8 +331,8 @@ void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, flush = (s->last_lit == s->lit_bufsize-1); \ } # define _tr_tally_dist(s, distance, length, flush) \ - { uch len = (length); \ - ush dist = (distance); \ + { uch len = (uch)(length); \ + ush dist = (ush)(distance); \ s->d_buf[s->last_lit] = dist; \ s->l_buf[s->last_lit++] = len; \ dist--; \ diff --git a/contrib/zlib/gzguts.h b/contrib/zlib/gzguts.h index d87659d03..6378d468a 100644 --- a/contrib/zlib/gzguts.h +++ b/contrib/zlib/gzguts.h @@ -1,5 +1,5 @@ /* gzguts.h -- zlib internal header definitions for gz* operations - * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -25,6 +25,10 @@ # include # include #endif + +#ifndef _POSIX_SOURCE +# define _POSIX_SOURCE +#endif #include #ifdef _WIN32 @@ -35,6 +39,10 @@ # include #endif +#if defined(_WIN32) +# define WIDECHAR +#endif + #ifdef WINAPI_FAMILY # define open _open # define read _read @@ -95,18 +103,19 @@ # endif #endif -/* unlike snprintf (which is required in C99, yet still not supported by - Microsoft more than a decade later!), _snprintf does not guarantee null - termination of the result -- however this is only used in gzlib.c where +/* unlike snprintf (which is required in C99), _snprintf does not guarantee + null termination of the result -- however this is only used in gzlib.c where the result is assured to fit in the space provided */ -#ifdef _MSC_VER +#if defined(_MSC_VER) && _MSC_VER < 1900 # define snprintf _snprintf #endif #ifndef local # define local static #endif -/* compile with -Dlocal if your debugger can't find static symbols */ +/* since "static" is used to mean two completely different things in C, we + define "local" for the non-static meaning of "static", for readability + (compile with -Dlocal if your debugger can't find static symbols) */ /* gz* functions always use library allocation functions */ #ifndef STDC @@ -170,7 +179,7 @@ typedef struct { char *path; /* path or fd for error messages */ unsigned size; /* buffer size, zero if not allocated yet */ unsigned want; /* requested buffer size, default is GZBUFSIZE */ - unsigned char *in; /* input buffer */ + unsigned char *in; /* input buffer (double-sized when writing) */ unsigned char *out; /* output buffer (double-sized when reading) */ int direct; /* 0 if processing gzip, 1 if transparent */ /* just for reading */ diff --git a/contrib/zlib/gzlib.c b/contrib/zlib/gzlib.c index fae202ef8..4838bf047 100644 --- a/contrib/zlib/gzlib.c +++ b/contrib/zlib/gzlib.c @@ -1,5 +1,5 @@ /* gzlib.c -- zlib functions common to reading and writing gzip files - * Copyright (C) 2004, 2010, 2011, 2012, 2013 Mark Adler + * Copyright (C) 2004-2017 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -94,7 +94,7 @@ local gzFile gz_open(path, fd, mode) const char *mode; { gz_statep state; - size_t len; + z_size_t len; int oflag; #ifdef O_CLOEXEC int cloexec = 0; @@ -188,10 +188,10 @@ local gzFile gz_open(path, fd, mode) } /* save the path name for error messages */ -#ifdef _WIN32 +#ifdef WIDECHAR if (fd == -2) { len = wcstombs(NULL, path, 0); - if (len == (size_t)-1) + if (len == (z_size_t)-1) len = 0; } else @@ -202,7 +202,7 @@ local gzFile gz_open(path, fd, mode) free(state); return NULL; } -#ifdef _WIN32 +#ifdef WIDECHAR if (fd == -2) if (len) wcstombs(state->path, path, len + 1); @@ -211,7 +211,7 @@ local gzFile gz_open(path, fd, mode) else #endif #if !defined(NO_snprintf) && !defined(NO_vsnprintf) - snprintf(state->path, len + 1, "%s", (const char *)path); + (void)snprintf(state->path, len + 1, "%s", (const char *)path); #else strcpy(state->path, path); #endif @@ -239,7 +239,7 @@ local gzFile gz_open(path, fd, mode) /* open the file with the appropriate flags (or just use fd) */ state->fd = fd > -1 ? fd : ( -#ifdef _WIN32 +#ifdef WIDECHAR fd == -2 ? _wopen(path, oflag, 0666) : #endif open((const char *)path, oflag, 0666)); @@ -248,8 +248,10 @@ local gzFile gz_open(path, fd, mode) free(state); return NULL; } - if (state->mode == GZ_APPEND) + if (state->mode == GZ_APPEND) { + LSEEK(state->fd, 0, SEEK_END); /* so gzoffset() is correct */ state->mode = GZ_WRITE; /* simplify later checks */ + } /* save the current position for rewinding (only if reading) */ if (state->mode == GZ_READ) { @@ -291,7 +293,7 @@ gzFile ZEXPORT gzdopen(fd, mode) if (fd == -1 || (path = (char *)malloc(7 + 3 * sizeof(int))) == NULL) return NULL; #if !defined(NO_snprintf) && !defined(NO_vsnprintf) - snprintf(path, 7 + 3 * sizeof(int), "", fd); /* for debugging */ + (void)snprintf(path, 7 + 3 * sizeof(int), "", fd); #else sprintf(path, "", fd); /* for debugging */ #endif @@ -301,7 +303,7 @@ gzFile ZEXPORT gzdopen(fd, mode) } /* -- see zlib.h -- */ -#ifdef _WIN32 +#ifdef WIDECHAR gzFile ZEXPORT gzopen_w(path, mode) const wchar_t *path; const char *mode; @@ -329,6 +331,8 @@ int ZEXPORT gzbuffer(file, size) return -1; /* check and set requested size */ + if ((size << 1) < size) + return -1; /* need to be able to double it */ if (size < 2) size = 2; /* need two bytes to check magic header */ state->want = size; @@ -393,7 +397,7 @@ z_off64_t ZEXPORT gzseek64(file, offset, whence) /* if within raw area while reading, just go there */ if (state->mode == GZ_READ && state->how == COPY && state->x.pos + offset >= 0) { - ret = LSEEK(state->fd, offset - state->x.have, SEEK_CUR); + ret = LSEEK(state->fd, offset - (z_off64_t)state->x.have, SEEK_CUR); if (ret == -1) return -1; state->x.have = 0; @@ -604,14 +608,13 @@ void ZLIB_INTERNAL gz_error(state, err, msg) return; } #if !defined(NO_snprintf) && !defined(NO_vsnprintf) - snprintf(state->msg, strlen(state->path) + strlen(msg) + 3, - "%s%s%s", state->path, ": ", msg); + (void)snprintf(state->msg, strlen(state->path) + strlen(msg) + 3, + "%s%s%s", state->path, ": ", msg); #else strcpy(state->msg, state->path); strcat(state->msg, ": "); strcat(state->msg, msg); #endif - return; } #ifndef INT_MAX diff --git a/contrib/zlib/gzread.c b/contrib/zlib/gzread.c index bf4538eb2..f94abfed3 100644 --- a/contrib/zlib/gzread.c +++ b/contrib/zlib/gzread.c @@ -1,5 +1,5 @@ /* gzread.c -- zlib functions for reading gzip files - * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -12,6 +12,7 @@ local int gz_look OF((gz_statep)); local int gz_decomp OF((gz_statep)); local int gz_fetch OF((gz_statep)); local int gz_skip OF((gz_statep, z_off64_t)); +local z_size_t gz_read OF((gz_statep, voidp, z_size_t)); /* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from state->fd, and update state->eof, state->err, and state->msg as appropriate. @@ -24,13 +25,17 @@ local int gz_load(state, buf, len, have) unsigned *have; { int ret; + unsigned get, max = ((unsigned)-1 >> 2) + 1; *have = 0; do { - ret = read(state->fd, buf + *have, len - *have); + get = len - *have; + if (get > max) + get = max; + ret = read(state->fd, buf + *have, get); if (ret <= 0) break; - *have += ret; + *have += (unsigned)ret; } while (*have < len); if (ret < 0) { gz_error(state, Z_ERRNO, zstrerror()); @@ -94,10 +99,8 @@ local int gz_look(state) state->in = (unsigned char *)malloc(state->want); state->out = (unsigned char *)malloc(state->want << 1); if (state->in == NULL || state->out == NULL) { - if (state->out != NULL) - free(state->out); - if (state->in != NULL) - free(state->in); + free(state->out); + free(state->in); gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } @@ -284,33 +287,17 @@ local int gz_skip(state, len) return 0; } -/* -- see zlib.h -- */ -int ZEXPORT gzread(file, buf, len) - gzFile file; - voidp buf; - unsigned len; -{ - unsigned got, n; +/* Read len bytes into buf from file, or less than len up to the end of the + input. Return the number of bytes read. If zero is returned, either the + end of file was reached, or there was an error. state->err must be + consulted in that case to determine which. */ +local z_size_t gz_read(state, buf, len) gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that we're reading and that there's no (serious) error */ - if (state->mode != GZ_READ || - (state->err != Z_OK && state->err != Z_BUF_ERROR)) - return -1; - - /* since an int is returned, make sure len fits in one, otherwise return - with an error (this avoids the flaw in the interface) */ - if ((int)len < 0) { - gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); - return -1; - } + voidp buf; + z_size_t len; +{ + z_size_t got; + unsigned n; /* if len is zero, avoid unnecessary operations */ if (len == 0) @@ -320,32 +307,38 @@ int ZEXPORT gzread(file, buf, len) if (state->seek) { state->seek = 0; if (gz_skip(state, state->skip) == -1) - return -1; + return 0; } /* get len bytes to buf, or less than len if at the end */ got = 0; do { + /* set n to the maximum amount of len that fits in an unsigned int */ + n = (unsigned)-1; + if (n > len) + n = (unsigned)len; + /* first just try copying data from the output buffer */ if (state->x.have) { - n = state->x.have > len ? len : state->x.have; + if (state->x.have < n) + n = state->x.have; memcpy(buf, state->x.next, n); state->x.next += n; state->x.have -= n; } /* output buffer empty -- return if we're at the end of the input */ - else if (state->eof && strm->avail_in == 0) { + else if (state->eof && state->strm.avail_in == 0) { state->past = 1; /* tried to read past end */ break; } /* need output data -- for small len or new stream load up our output buffer */ - else if (state->how == LOOK || len < (state->size << 1)) { + else if (state->how == LOOK || n < (state->size << 1)) { /* get more output, looking for header if required */ if (gz_fetch(state) == -1) - return -1; + return 0; continue; /* no progress yet -- go back to copy above */ /* the copy above assures that we will leave with space in the output buffer, allowing at least one gzungetc() to succeed */ @@ -353,16 +346,16 @@ int ZEXPORT gzread(file, buf, len) /* large len -- read directly into user buffer */ else if (state->how == COPY) { /* read directly */ - if (gz_load(state, (unsigned char *)buf, len, &n) == -1) - return -1; + if (gz_load(state, (unsigned char *)buf, n, &n) == -1) + return 0; } /* large len -- decompress directly into user buffer */ else { /* state->how == GZIP */ - strm->avail_out = len; - strm->next_out = (unsigned char *)buf; + state->strm.avail_out = n; + state->strm.next_out = (unsigned char *)buf; if (gz_decomp(state) == -1) - return -1; + return 0; n = state->x.have; state->x.have = 0; } @@ -374,8 +367,75 @@ int ZEXPORT gzread(file, buf, len) state->x.pos += n; } while (len); - /* return number of bytes read into user buffer (will fit in int) */ - return (int)got; + /* return number of bytes read into user buffer */ + return got; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzread(file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids a flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_STREAM_ERROR, "request does not fit in an int"); + return -1; + } + + /* read len or fewer bytes to buf */ + len = (unsigned)gz_read(state, buf, len); + + /* check for an error */ + if (len == 0 && state->err != Z_OK && state->err != Z_BUF_ERROR) + return -1; + + /* return the number of bytes read (this is assured to fit in an int) */ + return (int)len; +} + +/* -- see zlib.h -- */ +z_size_t ZEXPORT gzfread(buf, size, nitems, file) + voidp buf; + z_size_t size; + z_size_t nitems; + gzFile file; +{ + z_size_t len; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return 0; + + /* compute bytes to read -- error on overflow */ + len = nitems * size; + if (size && len / size != nitems) { + gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t"); + return 0; + } + + /* read len or fewer bytes to buf, return the number of full items read */ + return len ? gz_read(state, buf, len) / size : 0; } /* -- see zlib.h -- */ @@ -387,7 +447,6 @@ int ZEXPORT gzread(file, buf, len) int ZEXPORT gzgetc(file) gzFile file; { - int ret; unsigned char buf[1]; gz_statep state; @@ -408,9 +467,8 @@ int ZEXPORT gzgetc(file) return *(state->x.next)++; } - /* nothing there -- try gzread() */ - ret = gzread(file, buf, 1); - return ret < 1 ? -1 : buf[0]; + /* nothing there -- try gz_read() */ + return gz_read(state, buf, 1) < 1 ? -1 : buf[0]; } int ZEXPORT gzgetc_(file) @@ -451,7 +509,7 @@ int ZEXPORT gzungetc(c, file) if (state->x.have == 0) { state->x.have = 1; state->x.next = state->out + (state->size << 1) - 1; - state->x.next[0] = c; + state->x.next[0] = (unsigned char)c; state->x.pos--; state->past = 0; return c; @@ -473,7 +531,7 @@ int ZEXPORT gzungetc(c, file) } state->x.have++; state->x.next--; - state->x.next[0] = c; + state->x.next[0] = (unsigned char)c; state->x.pos--; state->past = 0; return c; diff --git a/contrib/zlib/gzwrite.c b/contrib/zlib/gzwrite.c index 3ed161f44..3560193b8 100644 --- a/contrib/zlib/gzwrite.c +++ b/contrib/zlib/gzwrite.c @@ -1,5 +1,5 @@ /* gzwrite.c -- zlib functions for writing gzip files - * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler + * Copyright (C) 2004-2017 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -9,17 +9,19 @@ local int gz_init OF((gz_statep)); local int gz_comp OF((gz_statep, int)); local int gz_zero OF((gz_statep, z_off64_t)); +local z_size_t gz_write OF((gz_statep, voidpc, z_size_t)); /* Initialize state for writing a gzip file. Mark initialization by setting - state->size to non-zero. Return -1 on failure or 0 on success. */ + state->size to non-zero. Return -1 on a memory allocation failure, or 0 on + success. */ local int gz_init(state) gz_statep state; { int ret; z_streamp strm = &(state->strm); - /* allocate input buffer */ - state->in = (unsigned char *)malloc(state->want); + /* allocate input buffer (double size for gzprintf) */ + state->in = (unsigned char *)malloc(state->want << 1); if (state->in == NULL) { gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; @@ -47,6 +49,7 @@ local int gz_init(state) gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } + strm->next_in = NULL; } /* mark state as initialized */ @@ -62,17 +65,17 @@ local int gz_init(state) } /* Compress whatever is at avail_in and next_in and write to the output file. - Return -1 if there is an error writing to the output file, otherwise 0. - flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH, - then the deflate() state is reset to start a new gzip stream. If gz->direct - is true, then simply write to the output file without compressing, and - ignore flush. */ + Return -1 if there is an error writing to the output file or if gz_init() + fails to allocate memory, otherwise 0. flush is assumed to be a valid + deflate() flush value. If flush is Z_FINISH, then the deflate() state is + reset to start a new gzip stream. If gz->direct is true, then simply write + to the output file without compressing, and ignore flush. */ local int gz_comp(state, flush) gz_statep state; int flush; { - int ret, got; - unsigned have; + int ret, writ; + unsigned have, put, max = ((unsigned)-1 >> 2) + 1; z_streamp strm = &(state->strm); /* allocate memory if this is the first time through */ @@ -81,12 +84,16 @@ local int gz_comp(state, flush) /* write directly if requested */ if (state->direct) { - got = write(state->fd, strm->next_in, strm->avail_in); - if (got < 0 || (unsigned)got != strm->avail_in) { - gz_error(state, Z_ERRNO, zstrerror()); - return -1; + while (strm->avail_in) { + put = strm->avail_in > max ? max : strm->avail_in; + writ = write(state->fd, strm->next_in, put); + if (writ < 0) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + strm->avail_in -= (unsigned)writ; + strm->next_in += writ; } - strm->avail_in = 0; return 0; } @@ -97,17 +104,21 @@ local int gz_comp(state, flush) doing Z_FINISH then don't write until we get to Z_STREAM_END */ if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && (flush != Z_FINISH || ret == Z_STREAM_END))) { - have = (unsigned)(strm->next_out - state->x.next); - if (have && ((got = write(state->fd, state->x.next, have)) < 0 || - (unsigned)got != have)) { - gz_error(state, Z_ERRNO, zstrerror()); - return -1; + while (strm->next_out > state->x.next) { + put = strm->next_out - state->x.next > (int)max ? max : + (unsigned)(strm->next_out - state->x.next); + writ = write(state->fd, state->x.next, put); + if (writ < 0) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + state->x.next += writ; } if (strm->avail_out == 0) { strm->avail_out = state->size; strm->next_out = state->out; + state->x.next = state->out; } - state->x.next = strm->next_out; } /* compress */ @@ -129,7 +140,8 @@ local int gz_comp(state, flush) return 0; } -/* Compress len zeros to output. Return -1 on error, 0 on success. */ +/* Compress len zeros to output. Return -1 on a write error or memory + allocation failure by gz_comp(), or 0 on success. */ local int gz_zero(state, len) gz_statep state; z_off64_t len; @@ -161,32 +173,14 @@ local int gz_zero(state, len) return 0; } -/* -- see zlib.h -- */ -int ZEXPORT gzwrite(file, buf, len) - gzFile file; - voidpc buf; - unsigned len; -{ - unsigned put = len; +/* Write len bytes from buf to file. Return the number of bytes written. If + the returned value is less than len, then there was an error. */ +local z_size_t gz_write(state, buf, len) gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return 0; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return 0; - - /* since an int is returned, make sure len fits in one, otherwise return - with an error (this avoids the flaw in the interface) */ - if ((int)len < 0) { - gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); - return 0; - } + voidpc buf; + z_size_t len; +{ + z_size_t put = len; /* if len is zero, avoid unnecessary operations */ if (len == 0) @@ -209,14 +203,15 @@ int ZEXPORT gzwrite(file, buf, len) do { unsigned have, copy; - if (strm->avail_in == 0) - strm->next_in = state->in; - have = (unsigned)((strm->next_in + strm->avail_in) - state->in); + if (state->strm.avail_in == 0) + state->strm.next_in = state->in; + have = (unsigned)((state->strm.next_in + state->strm.avail_in) - + state->in); copy = state->size - have; if (copy > len) - copy = len; + copy = (unsigned)len; memcpy(state->in + have, buf, copy); - strm->avail_in += copy; + state->strm.avail_in += copy; state->x.pos += copy; buf = (const char *)buf + copy; len -= copy; @@ -226,19 +221,83 @@ int ZEXPORT gzwrite(file, buf, len) } else { /* consume whatever's left in the input buffer */ - if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + if (state->strm.avail_in && gz_comp(state, Z_NO_FLUSH) == -1) return 0; /* directly compress user buffer to file */ - strm->avail_in = len; - strm->next_in = (z_const Bytef *)buf; - state->x.pos += len; - if (gz_comp(state, Z_NO_FLUSH) == -1) - return 0; + state->strm.next_in = (z_const Bytef *)buf; + do { + unsigned n = (unsigned)-1; + if (n > len) + n = (unsigned)len; + state->strm.avail_in = n; + state->x.pos += n; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + len -= n; + } while (len); } - /* input was all buffered or compressed (put will fit in int) */ - return (int)put; + /* input was all buffered or compressed */ + return put; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzwrite(file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids a flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); + return 0; + } + + /* write len bytes from buf (the return value will fit in an int) */ + return (int)gz_write(state, buf, len); +} + +/* -- see zlib.h -- */ +z_size_t ZEXPORT gzfwrite(buf, size, nitems, file) + voidpc buf; + z_size_t size; + z_size_t nitems; + gzFile file; +{ + z_size_t len; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* compute bytes to read -- error on overflow */ + len = nitems * size; + if (size && len / size != nitems) { + gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t"); + return 0; + } + + /* write len bytes to buf, return the number of full items written */ + return len ? gz_write(state, buf, len) / size : 0; } /* -- see zlib.h -- */ @@ -275,7 +334,7 @@ int ZEXPORT gzputc(file, c) strm->next_in = state->in; have = (unsigned)((strm->next_in + strm->avail_in) - state->in); if (have < state->size) { - state->in[have] = c; + state->in[have] = (unsigned char)c; strm->avail_in++; state->x.pos++; return c & 0xff; @@ -283,8 +342,8 @@ int ZEXPORT gzputc(file, c) } /* no room in buffer or not initialized, use gz_write() */ - buf[0] = c; - if (gzwrite(file, buf, 1) != 1) + buf[0] = (unsigned char)c; + if (gz_write(state, buf, 1) != 1) return -1; return c & 0xff; } @@ -294,13 +353,26 @@ int ZEXPORT gzputs(file, str) gzFile file; const char *str; { - int ret; - unsigned len; + z_size_t len, put; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return -1; /* write string */ - len = (unsigned)strlen(str); - ret = gzwrite(file, str, len); - return ret == 0 && len != 0 ? -1 : ret; + len = strlen(str); + if ((int)len < 0 || (unsigned)len != len) { + gz_error(state, Z_STREAM_ERROR, "string length does not fit in int"); + return -1; + } + put = gz_write(state, str, len); + return put < len ? -1 : (int)len; } #if defined(STDC) || defined(Z_HAVE_STDARG_H) @@ -309,63 +381,73 @@ int ZEXPORT gzputs(file, str) /* -- see zlib.h -- */ int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) { - int size, len; + int len; + unsigned left; + char *next; gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) - return -1; + return Z_STREAM_ERROR; state = (gz_statep)file; strm = &(state->strm); /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) - return 0; + return Z_STREAM_ERROR; /* make sure we have some buffer space */ if (state->size == 0 && gz_init(state) == -1) - return 0; + return state->err; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) - return 0; + return state->err; } - /* consume whatever's left in the input buffer */ - if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) - return 0; - - /* do the printf() into the input buffer, put length in len */ - size = (int)(state->size); - state->in[size - 1] = 0; + /* do the printf() into the input buffer, put length in len -- the input + buffer is double-sized just for this function, so there is guaranteed to + be state->size bytes available after the current contents */ + if (strm->avail_in == 0) + strm->next_in = state->in; + next = (char *)(state->in + (strm->next_in - state->in) + strm->avail_in); + next[state->size - 1] = 0; #ifdef NO_vsnprintf # ifdef HAS_vsprintf_void - (void)vsprintf((char *)(state->in), format, va); - for (len = 0; len < size; len++) - if (state->in[len] == 0) break; + (void)vsprintf(next, format, va); + for (len = 0; len < state->size; len++) + if (next[len] == 0) break; # else - len = vsprintf((char *)(state->in), format, va); + len = vsprintf(next, format, va); # endif #else # ifdef HAS_vsnprintf_void - (void)vsnprintf((char *)(state->in), size, format, va); - len = strlen((char *)(state->in)); + (void)vsnprintf(next, state->size, format, va); + len = strlen(next); # else - len = vsnprintf((char *)(state->in), size, format, va); + len = vsnprintf(next, state->size, format, va); # endif #endif /* check that printf() results fit in buffer */ - if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) + if (len == 0 || (unsigned)len >= state->size || next[state->size - 1] != 0) return 0; - /* update buffer and position, defer compression until needed */ - strm->avail_in = (unsigned)len; - strm->next_in = state->in; + /* update buffer and position, compress first half if past that */ + strm->avail_in += (unsigned)len; state->x.pos += len; + if (strm->avail_in >= state->size) { + left = strm->avail_in - state->size; + strm->avail_in = state->size; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return state->err; + memcpy(state->in, state->in + state->size, left); + strm->next_in = state->in; + strm->avail_in = left; + } return len; } @@ -390,73 +472,82 @@ int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; { - int size, len; + unsigned len, left; + char *next; gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) - return -1; + return Z_STREAM_ERROR; state = (gz_statep)file; strm = &(state->strm); /* check that can really pass pointer in ints */ if (sizeof(int) != sizeof(void *)) - return 0; + return Z_STREAM_ERROR; /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) - return 0; + return Z_STREAM_ERROR; /* make sure we have some buffer space */ if (state->size == 0 && gz_init(state) == -1) - return 0; + return state->error; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) - return 0; + return state->error; } - /* consume whatever's left in the input buffer */ - if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) - return 0; - - /* do the printf() into the input buffer, put length in len */ - size = (int)(state->size); - state->in[size - 1] = 0; + /* do the printf() into the input buffer, put length in len -- the input + buffer is double-sized just for this function, so there is guaranteed to + be state->size bytes available after the current contents */ + if (strm->avail_in == 0) + strm->next_in = state->in; + next = (char *)(strm->next_in + strm->avail_in); + next[state->size - 1] = 0; #ifdef NO_snprintf # ifdef HAS_sprintf_void - sprintf((char *)(state->in), format, a1, a2, a3, a4, a5, a6, a7, a8, - a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, + a13, a14, a15, a16, a17, a18, a19, a20); for (len = 0; len < size; len++) - if (state->in[len] == 0) break; + if (next[len] == 0) + break; # else - len = sprintf((char *)(state->in), format, a1, a2, a3, a4, a5, a6, a7, a8, - a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, + a12, a13, a14, a15, a16, a17, a18, a19, a20); # endif #else # ifdef HAS_snprintf_void - snprintf((char *)(state->in), size, format, a1, a2, a3, a4, a5, a6, a7, a8, - a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); - len = strlen((char *)(state->in)); + snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, + a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(next); # else - len = snprintf((char *)(state->in), size, format, a1, a2, a3, a4, a5, a6, - a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, - a19, a20); + len = snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); # endif #endif /* check that printf() results fit in buffer */ - if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) + if (len == 0 || len >= state->size || next[state->size - 1] != 0) return 0; - /* update buffer and position, defer compression until needed */ - strm->avail_in = (unsigned)len; - strm->next_in = state->in; + /* update buffer and position, compress first half if past that */ + strm->avail_in += len; state->x.pos += len; - return len; + if (strm->avail_in >= state->size) { + left = strm->avail_in - state->size; + strm->avail_in = state->size; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return state->err; + memcpy(state->in, state->in + state->size, left); + strm->next_in = state->in; + strm->avail_in = left; + } + return (int)len; } #endif @@ -470,7 +561,7 @@ int ZEXPORT gzflush(file, flush) /* get internal structure */ if (file == NULL) - return -1; + return Z_STREAM_ERROR; state = (gz_statep)file; /* check that we're writing and that there's no error */ @@ -485,11 +576,11 @@ int ZEXPORT gzflush(file, flush) if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) - return -1; + return state->err; } /* compress remaining data with requested flush */ - gz_comp(state, flush); + (void)gz_comp(state, flush); return state->err; } @@ -520,13 +611,13 @@ int ZEXPORT gzsetparams(file, level, strategy) if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) - return -1; + return state->err; } /* change compression parameters for subsequent input */ if (state->size) { /* flush previous input with previous parameters before changing */ - if (strm->avail_in && gz_comp(state, Z_PARTIAL_FLUSH) == -1) + if (strm->avail_in && gz_comp(state, Z_BLOCK) == -1) return state->err; deflateParams(strm, level, strategy); } diff --git a/contrib/zlib/infback.c b/contrib/zlib/infback.c index f3833c2e4..59679ecbf 100644 --- a/contrib/zlib/infback.c +++ b/contrib/zlib/infback.c @@ -1,5 +1,5 @@ /* infback.c -- inflate using a call-back interface - * Copyright (C) 1995-2011 Mark Adler + * Copyright (C) 1995-2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -61,7 +61,7 @@ int stream_size; Tracev((stderr, "inflate: allocated\n")); strm->state = (struct internal_state FAR *)state; state->dmax = 32768U; - state->wbits = windowBits; + state->wbits = (uInt)windowBits; state->wsize = 1U << windowBits; state->window = window; state->wnext = 0; diff --git a/contrib/zlib/inffast.c b/contrib/zlib/inffast.c index bda59ceb6..1fec7f363 100644 --- a/contrib/zlib/inffast.c +++ b/contrib/zlib/inffast.c @@ -1,5 +1,5 @@ /* inffast.c -- fast decoding - * Copyright (C) 1995-2008, 2010, 2013 Mark Adler + * Copyright (C) 1995-2017 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -8,26 +8,9 @@ #include "inflate.h" #include "inffast.h" -#ifndef ASMINF - -/* Allow machine dependent optimization for post-increment or pre-increment. - Based on testing to date, - Pre-increment preferred for: - - PowerPC G3 (Adler) - - MIPS R5000 (Randers-Pehrson) - Post-increment preferred for: - - none - No measurable difference: - - Pentium III (Anderson) - - M68060 (Nikl) - */ -#ifdef POSTINC -# define OFF 0 -# define PUP(a) *(a)++ +#ifdef ASMINF +# pragma message("Assembler code may have bugs -- use at your own risk") #else -# define OFF 1 -# define PUP(a) *++(a) -#endif /* Decode literal, length, and distance codes and write out the resulting @@ -87,7 +70,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ code const FAR *dcode; /* local strm->distcode */ unsigned lmask; /* mask for first level of length codes */ unsigned dmask; /* mask for first level of distance codes */ - code here; /* retrieved table entry */ + code const *here; /* retrieved table entry */ unsigned op; /* code bits, operation, extra bits, or */ /* window position, window bytes to copy */ unsigned len; /* match length, unused bytes */ @@ -96,9 +79,9 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ /* copy state to local variables */ state = (struct inflate_state FAR *)strm->state; - in = strm->next_in - OFF; + in = strm->next_in; last = in + (strm->avail_in - 5); - out = strm->next_out - OFF; + out = strm->next_out; beg = out - (start - strm->avail_out); end = out + (strm->avail_out - 257); #ifdef INFLATE_STRICT @@ -119,29 +102,29 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ input data or output space */ do { if (bits < 15) { - hold += (unsigned long)(PUP(in)) << bits; + hold += (unsigned long)(*in++) << bits; bits += 8; - hold += (unsigned long)(PUP(in)) << bits; + hold += (unsigned long)(*in++) << bits; bits += 8; } - here = lcode[hold & lmask]; + here = lcode + (hold & lmask); dolen: - op = (unsigned)(here.bits); + op = (unsigned)(here->bits); hold >>= op; bits -= op; - op = (unsigned)(here.op); + op = (unsigned)(here->op); if (op == 0) { /* literal */ - Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + Tracevv((stderr, here->val >= 0x20 && here->val < 0x7f ? "inflate: literal '%c'\n" : - "inflate: literal 0x%02x\n", here.val)); - PUP(out) = (unsigned char)(here.val); + "inflate: literal 0x%02x\n", here->val)); + *out++ = (unsigned char)(here->val); } else if (op & 16) { /* length base */ - len = (unsigned)(here.val); + len = (unsigned)(here->val); op &= 15; /* number of extra bits */ if (op) { if (bits < op) { - hold += (unsigned long)(PUP(in)) << bits; + hold += (unsigned long)(*in++) << bits; bits += 8; } len += (unsigned)hold & ((1U << op) - 1); @@ -150,25 +133,25 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ } Tracevv((stderr, "inflate: length %u\n", len)); if (bits < 15) { - hold += (unsigned long)(PUP(in)) << bits; + hold += (unsigned long)(*in++) << bits; bits += 8; - hold += (unsigned long)(PUP(in)) << bits; + hold += (unsigned long)(*in++) << bits; bits += 8; } - here = dcode[hold & dmask]; + here = dcode + (hold & dmask); dodist: - op = (unsigned)(here.bits); + op = (unsigned)(here->bits); hold >>= op; bits -= op; - op = (unsigned)(here.op); + op = (unsigned)(here->op); if (op & 16) { /* distance base */ - dist = (unsigned)(here.val); + dist = (unsigned)(here->val); op &= 15; /* number of extra bits */ if (bits < op) { - hold += (unsigned long)(PUP(in)) << bits; + hold += (unsigned long)(*in++) << bits; bits += 8; if (bits < op) { - hold += (unsigned long)(PUP(in)) << bits; + hold += (unsigned long)(*in++) << bits; bits += 8; } } @@ -196,30 +179,30 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR if (len <= op - whave) { do { - PUP(out) = 0; + *out++ = 0; } while (--len); continue; } len -= op - whave; do { - PUP(out) = 0; + *out++ = 0; } while (--op > whave); if (op == 0) { from = out - dist; do { - PUP(out) = PUP(from); + *out++ = *from++; } while (--len); continue; } #endif } - from = window - OFF; + from = window; if (wnext == 0) { /* very common case */ from += wsize - op; if (op < len) { /* some from window */ len -= op; do { - PUP(out) = PUP(from); + *out++ = *from++; } while (--op); from = out - dist; /* rest from output */ } @@ -230,14 +213,14 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ if (op < len) { /* some from end of window */ len -= op; do { - PUP(out) = PUP(from); + *out++ = *from++; } while (--op); - from = window - OFF; + from = window; if (wnext < len) { /* some from start of window */ op = wnext; len -= op; do { - PUP(out) = PUP(from); + *out++ = *from++; } while (--op); from = out - dist; /* rest from output */ } @@ -248,40 +231,40 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ if (op < len) { /* some from window */ len -= op; do { - PUP(out) = PUP(from); + *out++ = *from++; } while (--op); from = out - dist; /* rest from output */ } } while (len > 2) { - PUP(out) = PUP(from); - PUP(out) = PUP(from); - PUP(out) = PUP(from); + *out++ = *from++; + *out++ = *from++; + *out++ = *from++; len -= 3; } if (len) { - PUP(out) = PUP(from); + *out++ = *from++; if (len > 1) - PUP(out) = PUP(from); + *out++ = *from++; } } else { from = out - dist; /* copy direct from output */ do { /* minimum length is three */ - PUP(out) = PUP(from); - PUP(out) = PUP(from); - PUP(out) = PUP(from); + *out++ = *from++; + *out++ = *from++; + *out++ = *from++; len -= 3; } while (len > 2); if (len) { - PUP(out) = PUP(from); + *out++ = *from++; if (len > 1) - PUP(out) = PUP(from); + *out++ = *from++; } } } else if ((op & 64) == 0) { /* 2nd level distance code */ - here = dcode[here.val + (hold & ((1U << op) - 1))]; + here = dcode + here->val + (hold & ((1U << op) - 1)); goto dodist; } else { @@ -291,7 +274,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ } } else if ((op & 64) == 0) { /* 2nd level length code */ - here = lcode[here.val + (hold & ((1U << op) - 1))]; + here = lcode + here->val + (hold & ((1U << op) - 1)); goto dolen; } else if (op & 32) { /* end-of-block */ @@ -313,8 +296,8 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ hold &= (1U << bits) - 1; /* update state and return */ - strm->next_in = in + OFF; - strm->next_out = out + OFF; + strm->next_in = in; + strm->next_out = out; strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); strm->avail_out = (unsigned)(out < end ? 257 + (end - out) : 257 - (out - end)); diff --git a/contrib/zlib/inflate.c b/contrib/zlib/inflate.c index 870f89bb4..575fcdf82 100644 --- a/contrib/zlib/inflate.c +++ b/contrib/zlib/inflate.c @@ -1,5 +1,5 @@ /* inflate.c -- zlib decompression - * Copyright (C) 1995-2012 Mark Adler + * Copyright (C) 1995-2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -92,6 +92,7 @@ #endif /* function prototypes */ +local int inflateStateCheck OF((z_streamp strm)); local void fixedtables OF((struct inflate_state FAR *state)); local int updatewindow OF((z_streamp strm, const unsigned char FAR *end, unsigned copy)); @@ -101,12 +102,26 @@ local int updatewindow OF((z_streamp strm, const unsigned char FAR *end, local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf, unsigned len)); +local int inflateStateCheck(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) + return 1; + state = (struct inflate_state FAR *)strm->state; + if (state == Z_NULL || state->strm != strm || + state->mode < HEAD || state->mode > SYNC) + return 1; + return 0; +} + int ZEXPORT inflateResetKeep(strm) z_streamp strm; { struct inflate_state FAR *state; - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; strm->total_in = strm->total_out = state->total = 0; strm->msg = Z_NULL; @@ -131,7 +146,7 @@ z_streamp strm; { struct inflate_state FAR *state; - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; state->wsize = 0; state->whave = 0; @@ -147,7 +162,7 @@ int windowBits; struct inflate_state FAR *state; /* get the state */ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; /* extract wrap request from windowBits parameter */ @@ -156,7 +171,7 @@ int windowBits; windowBits = -windowBits; } else { - wrap = (windowBits >> 4) + 1; + wrap = (windowBits >> 4) + 5; #ifdef GUNZIP if (windowBits < 48) windowBits &= 15; @@ -210,7 +225,9 @@ int stream_size; if (state == Z_NULL) return Z_MEM_ERROR; Tracev((stderr, "inflate: allocated\n")); strm->state = (struct internal_state FAR *)state; + state->strm = strm; state->window = Z_NULL; + state->mode = HEAD; /* to pass state test in inflateReset2() */ ret = inflateReset2(strm, windowBits); if (ret != Z_OK) { ZFREE(strm, state); @@ -234,17 +251,17 @@ int value; { struct inflate_state FAR *state; - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (bits < 0) { state->hold = 0; state->bits = 0; return Z_OK; } - if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + if (bits > 16 || state->bits + (uInt)bits > 32) return Z_STREAM_ERROR; value &= (1L << bits) - 1; - state->hold += value << state->bits; - state->bits += bits; + state->hold += (unsigned)value << state->bits; + state->bits += (uInt)bits; return Z_OK; } @@ -625,7 +642,7 @@ int flush; static const unsigned short order[19] = /* permutation of code lengths */ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; - if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + if (inflateStateCheck(strm) || strm->next_out == Z_NULL || (strm->next_in == Z_NULL && strm->avail_in != 0)) return Z_STREAM_ERROR; @@ -645,6 +662,8 @@ int flush; NEEDBITS(16); #ifdef GUNZIP if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + if (state->wbits == 0) + state->wbits = 15; state->check = crc32(0L, Z_NULL, 0); CRC2(state->check, hold); INITBITS(); @@ -672,7 +691,7 @@ int flush; len = BITS(4) + 8; if (state->wbits == 0) state->wbits = len; - else if (len > state->wbits) { + if (len > 15 || len > state->wbits) { strm->msg = (char *)"invalid window size"; state->mode = BAD; break; @@ -699,14 +718,16 @@ int flush; } if (state->head != Z_NULL) state->head->text = (int)((hold >> 8) & 1); - if (state->flags & 0x0200) CRC2(state->check, hold); + if ((state->flags & 0x0200) && (state->wrap & 4)) + CRC2(state->check, hold); INITBITS(); state->mode = TIME; case TIME: NEEDBITS(32); if (state->head != Z_NULL) state->head->time = hold; - if (state->flags & 0x0200) CRC4(state->check, hold); + if ((state->flags & 0x0200) && (state->wrap & 4)) + CRC4(state->check, hold); INITBITS(); state->mode = OS; case OS: @@ -715,7 +736,8 @@ int flush; state->head->xflags = (int)(hold & 0xff); state->head->os = (int)(hold >> 8); } - if (state->flags & 0x0200) CRC2(state->check, hold); + if ((state->flags & 0x0200) && (state->wrap & 4)) + CRC2(state->check, hold); INITBITS(); state->mode = EXLEN; case EXLEN: @@ -724,7 +746,8 @@ int flush; state->length = (unsigned)(hold); if (state->head != Z_NULL) state->head->extra_len = (unsigned)hold; - if (state->flags & 0x0200) CRC2(state->check, hold); + if ((state->flags & 0x0200) && (state->wrap & 4)) + CRC2(state->check, hold); INITBITS(); } else if (state->head != Z_NULL) @@ -742,7 +765,7 @@ int flush; len + copy > state->head->extra_max ? state->head->extra_max - len : copy); } - if (state->flags & 0x0200) + if ((state->flags & 0x0200) && (state->wrap & 4)) state->check = crc32(state->check, next, copy); have -= copy; next += copy; @@ -761,9 +784,9 @@ int flush; if (state->head != Z_NULL && state->head->name != Z_NULL && state->length < state->head->name_max) - state->head->name[state->length++] = len; + state->head->name[state->length++] = (Bytef)len; } while (len && copy < have); - if (state->flags & 0x0200) + if ((state->flags & 0x0200) && (state->wrap & 4)) state->check = crc32(state->check, next, copy); have -= copy; next += copy; @@ -782,9 +805,9 @@ int flush; if (state->head != Z_NULL && state->head->comment != Z_NULL && state->length < state->head->comm_max) - state->head->comment[state->length++] = len; + state->head->comment[state->length++] = (Bytef)len; } while (len && copy < have); - if (state->flags & 0x0200) + if ((state->flags & 0x0200) && (state->wrap & 4)) state->check = crc32(state->check, next, copy); have -= copy; next += copy; @@ -796,7 +819,7 @@ int flush; case HCRC: if (state->flags & 0x0200) { NEEDBITS(16); - if (hold != (state->check & 0xffff)) { + if ((state->wrap & 4) && hold != (state->check & 0xffff)) { strm->msg = (char *)"header crc mismatch"; state->mode = BAD; break; @@ -1177,11 +1200,11 @@ int flush; out -= left; strm->total_out += out; state->total += out; - if (out) + if ((state->wrap & 4) && out) strm->adler = state->check = UPDATE(state->check, put - out, out); out = left; - if (( + if ((state->wrap & 4) && ( #ifdef GUNZIP state->flags ? hold : #endif @@ -1240,10 +1263,10 @@ int flush; strm->total_in += in; strm->total_out += out; state->total += out; - if (state->wrap && out) + if ((state->wrap & 4) && out) strm->adler = state->check = UPDATE(state->check, strm->next_out - out, out); - strm->data_type = state->bits + (state->last ? 64 : 0) + + strm->data_type = (int)state->bits + (state->last ? 64 : 0) + (state->mode == TYPE ? 128 : 0) + (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) @@ -1255,7 +1278,7 @@ int ZEXPORT inflateEnd(strm) z_streamp strm; { struct inflate_state FAR *state; - if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (state->window != Z_NULL) ZFREE(strm, state->window); @@ -1273,7 +1296,7 @@ uInt *dictLength; struct inflate_state FAR *state; /* check state */ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; /* copy dictionary */ @@ -1298,7 +1321,7 @@ uInt dictLength; int ret; /* check state */ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (state->wrap != 0 && state->mode != DICT) return Z_STREAM_ERROR; @@ -1330,7 +1353,7 @@ gz_headerp head; struct inflate_state FAR *state; /* check state */ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; @@ -1383,7 +1406,7 @@ z_streamp strm; struct inflate_state FAR *state; /* check parameters */ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; @@ -1410,6 +1433,8 @@ z_streamp strm; /* return no joy or set up to restart inflate() on a new block */ if (state->have != 4) return Z_DATA_ERROR; + if (state->mode == HEAD) + state->wrap = 0; /* never processed header, so assume raw */ in = strm->total_in; out = strm->total_out; inflateReset(strm); strm->total_in = in; strm->total_out = out; @@ -1430,7 +1455,7 @@ z_streamp strm; { struct inflate_state FAR *state; - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; return state->mode == STORED && state->bits == 0; } @@ -1445,8 +1470,7 @@ z_streamp source; unsigned wsize; /* check input */ - if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || - source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + if (inflateStateCheck(source) || dest == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)source->state; @@ -1467,6 +1491,7 @@ z_streamp source; /* copy state */ zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state)); + copy->strm = dest; if (state->lencode >= state->codes && state->lencode <= state->codes + ENOUGH - 1) { copy->lencode = copy->codes + (state->lencode - state->codes); @@ -1488,25 +1513,51 @@ int subvert; { struct inflate_state FAR *state; - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; - state->sane = !subvert; #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + state->sane = !subvert; return Z_OK; #else + (void)subvert; state->sane = 1; return Z_DATA_ERROR; #endif } +int ZEXPORT inflateValidate(strm, check) +z_streamp strm; +int check; +{ + struct inflate_state FAR *state; + + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (check && state->wrap) + state->wrap |= 4; + else + state->wrap &= ~4; + return Z_OK; +} + long ZEXPORT inflateMark(strm) z_streamp strm; { struct inflate_state FAR *state; - if (strm == Z_NULL || strm->state == Z_NULL) return -1L << 16; + if (inflateStateCheck(strm)) + return -(1L << 16); state = (struct inflate_state FAR *)strm->state; - return ((long)(state->back) << 16) + + return (long)(((unsigned long)((long)state->back)) << 16) + (state->mode == COPY ? state->length : (state->mode == MATCH ? state->was - state->length : 0)); } + +unsigned long ZEXPORT inflateCodesUsed(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (inflateStateCheck(strm)) return (unsigned long)-1; + state = (struct inflate_state FAR *)strm->state; + return (unsigned long)(state->next - state->codes); +} diff --git a/contrib/zlib/inflate.h b/contrib/zlib/inflate.h index 95f4986d4..a46cce6b6 100644 --- a/contrib/zlib/inflate.h +++ b/contrib/zlib/inflate.h @@ -1,5 +1,5 @@ /* inflate.h -- internal inflate state definition - * Copyright (C) 1995-2009 Mark Adler + * Copyright (C) 1995-2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -18,7 +18,7 @@ /* Possible inflate modes between inflate() calls */ typedef enum { - HEAD, /* i: waiting for magic header */ + HEAD = 16180, /* i: waiting for magic header */ FLAGS, /* i: waiting for method and flags (gzip) */ TIME, /* i: waiting for modification time (gzip) */ OS, /* i: waiting for extra flags and operating system (gzip) */ @@ -77,11 +77,14 @@ typedef enum { CHECK -> LENGTH -> DONE */ -/* state maintained between inflate() calls. Approximately 10K bytes. */ +/* State maintained between inflate() calls -- approximately 7K bytes, not + including the allocated sliding window, which is up to 32K bytes. */ struct inflate_state { + z_streamp strm; /* pointer back to this zlib stream */ inflate_mode mode; /* current inflate mode */ int last; /* true if processing last block */ - int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip, + bit 2 true to validate check value */ int havedict; /* true if dictionary provided */ int flags; /* gzip header method and flags (0 if zlib) */ unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ diff --git a/contrib/zlib/inftrees.c b/contrib/zlib/inftrees.c index 44d89cf24..716c98842 100644 --- a/contrib/zlib/inftrees.c +++ b/contrib/zlib/inftrees.c @@ -1,5 +1,5 @@ /* inftrees.c -- generate Huffman trees for efficient decoding - * Copyright (C) 1995-2013 Mark Adler + * Copyright (C) 1995-2017 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -9,7 +9,7 @@ #define MAXBITS 15 const char inflate_copyright[] = - " inflate 1.2.8 Copyright 1995-2013 Mark Adler "; + " inflate 1.2.11.1 Copyright 1995-2017 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -54,7 +54,7 @@ unsigned short FAR *work; code FAR *next; /* next available space in table */ const unsigned short FAR *base; /* base value table to use */ const unsigned short FAR *extra; /* extra bits table to use */ - int end; /* use base and extra for symbol > end */ + unsigned match; /* use base and extra for symbol >= match */ unsigned short count[MAXBITS+1]; /* number of codes of each length */ unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ static const unsigned short lbase[31] = { /* Length codes 257..285 base */ @@ -62,7 +62,7 @@ unsigned short FAR *work; 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; static const unsigned short lext[31] = { /* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, - 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78}; + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 198, 196}; static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, @@ -181,19 +181,17 @@ unsigned short FAR *work; switch (type) { case CODES: base = extra = work; /* dummy value--not used */ - end = 19; + match = 20; break; case LENS: base = lbase; - base -= 257; extra = lext; - extra -= 257; - end = 256; + match = 257; break; - default: /* DISTS */ + default: /* DISTS */ base = dbase; extra = dext; - end = -1; + match = 0; } /* initialize state for loop */ @@ -216,13 +214,13 @@ unsigned short FAR *work; for (;;) { /* create table entry */ here.bits = (unsigned char)(len - drop); - if ((int)(work[sym]) < end) { + if (work[sym] + 1U < match) { here.op = (unsigned char)0; here.val = work[sym]; } - else if ((int)(work[sym]) > end) { - here.op = (unsigned char)(extra[work[sym]]); - here.val = base[work[sym]]; + else if (work[sym] >= match) { + here.op = (unsigned char)(extra[work[sym] - match]); + here.val = base[work[sym] - match]; } else { here.op = (unsigned char)(32 + 64); /* end of block */ diff --git a/contrib/zlib/make_vms.com b/contrib/zlib/make_vms.com new file mode 100644 index 000000000..65e9d0cbc --- /dev/null +++ b/contrib/zlib/make_vms.com @@ -0,0 +1,867 @@ +$! make libz under VMS written by +$! Martin P.J. Zinser +$! +$! In case of problems with the install you might contact me at +$! zinser@zinser.no-ip.info(preferred) or +$! martin.zinser@eurexchange.com (work) +$! +$! Make procedure history for Zlib +$! +$!------------------------------------------------------------------------------ +$! Version history +$! 0.01 20060120 First version to receive a number +$! 0.02 20061008 Adapt to new Makefile.in +$! 0.03 20091224 Add support for large file check +$! 0.04 20100110 Add new gzclose, gzlib, gzread, gzwrite +$! 0.05 20100221 Exchange zlibdefs.h by zconf.h.in +$! 0.06 20120111 Fix missing amiss_err, update zconf_h.in, fix new exmples +$! subdir path, update module search in makefile.in +$! 0.07 20120115 Triggered by work done by Alexey Chupahin completly redesigned +$! shared image creation +$! 0.08 20120219 Make it work on VAX again, pre-load missing symbols to shared +$! image +$! 0.09 20120305 SMS. P1 sets builder ("MMK", "MMS", " " (built-in)). +$! "" -> automatic, preference: MMK, MMS, built-in. +$! +$ on error then goto err_exit +$! +$ true = 1 +$ false = 0 +$ tmpnam = "temp_" + f$getjpi("","pid") +$ tt = tmpnam + ".txt" +$ tc = tmpnam + ".c" +$ th = tmpnam + ".h" +$ define/nolog tconfig 'th' +$ its_decc = false +$ its_vaxc = false +$ its_gnuc = false +$ s_case = False +$! +$! Setup variables holding "config" information +$! +$ Make = "''p1'" +$ name = "Zlib" +$ version = "?.?.?" +$ v_string = "ZLIB_VERSION" +$ v_file = "zlib.h" +$ ccopt = "/include = []" +$ lopts = "" +$ dnsrl = "" +$ aconf_in_file = "zconf.h.in#zconf.h_in#zconf_h.in" +$ conf_check_string = "" +$ linkonly = false +$ optfile = name + ".opt" +$ mapfile = name + ".map" +$ libdefs = "" +$ vax = f$getsyi("HW_MODEL").lt.1024 +$ axp = f$getsyi("HW_MODEL").ge.1024 .and. f$getsyi("HW_MODEL").lt.4096 +$ ia64 = f$getsyi("HW_MODEL").ge.4096 +$! +$! 2012-03-05 SMS. +$! Why is this needed? And if it is needed, why not simply ".not. vax"? +$! +$!!! if axp .or. ia64 then set proc/parse=extended +$! +$ whoami = f$parse(f$environment("Procedure"),,,,"NO_CONCEAL") +$ mydef = F$parse(whoami,,,"DEVICE") +$ mydir = f$parse(whoami,,,"DIRECTORY") - "][" +$ myproc = f$parse(whoami,,,"Name") + f$parse(whoami,,,"type") +$! +$! Check for MMK/MMS +$! +$ if (Make .eqs. "") +$ then +$ If F$Search ("Sys$System:MMS.EXE") .nes. "" Then Make = "MMS" +$ If F$Type (MMK) .eqs. "STRING" Then Make = "MMK" +$ else +$ Make = f$edit( Make, "trim") +$ endif +$! +$ gosub find_version +$! +$ open/write topt tmp.opt +$ open/write optf 'optfile' +$! +$ gosub check_opts +$! +$! Look for the compiler used +$! +$ gosub check_compiler +$ close topt +$ close optf +$! +$ if its_decc +$ then +$ ccopt = "/prefix=all" + ccopt +$ if f$trnlnm("SYS") .eqs. "" +$ then +$ if axp +$ then +$ define sys sys$library: +$ else +$ ccopt = "/decc" + ccopt +$ define sys decc$library_include: +$ endif +$ endif +$! +$! 2012-03-05 SMS. +$! Why /NAMES = AS_IS? Why not simply ".not. vax"? And why not on VAX? +$! +$ if axp .or. ia64 +$ then +$ ccopt = ccopt + "/name=as_is/opt=(inline=speed)" +$ s_case = true +$ endif +$ endif +$ if its_vaxc .or. its_gnuc +$ then +$ if f$trnlnm("SYS").eqs."" then define sys sys$library: +$ endif +$! +$! Build a fake configure input header +$! +$ open/write conf_hin config.hin +$ write conf_hin "#undef _LARGEFILE64_SOURCE" +$ close conf_hin +$! +$! +$ i = 0 +$FIND_ACONF: +$ fname = f$element(i,"#",aconf_in_file) +$ if fname .eqs. "#" then goto AMISS_ERR +$ if f$search(fname) .eqs. "" +$ then +$ i = i + 1 +$ goto find_aconf +$ endif +$ open/read/err=aconf_err aconf_in 'fname' +$ open/write aconf zconf.h +$ACONF_LOOP: +$ read/end_of_file=aconf_exit aconf_in line +$ work = f$edit(line, "compress,trim") +$ if f$extract(0,6,work) .nes. "#undef" +$ then +$ if f$extract(0,12,work) .nes. "#cmakedefine" +$ then +$ write aconf line +$ endif +$ else +$ cdef = f$element(1," ",work) +$ gosub check_config +$ endif +$ goto aconf_loop +$ACONF_EXIT: +$ write aconf "" +$ write aconf "/* VMS specifics added by make_vms.com: */" +$ write aconf "#define VMS 1" +$ write aconf "#include " +$ write aconf "#include " +$ write aconf "#ifdef _LARGEFILE" +$ write aconf "# define off64_t __off64_t" +$ write aconf "# define fopen64 fopen" +$ write aconf "# define fseeko64 fseeko" +$ write aconf "# define lseek64 lseek" +$ write aconf "# define ftello64 ftell" +$ write aconf "#endif" +$ write aconf "#if !defined( __VAX) && (__CRTL_VER >= 70312000)" +$ write aconf "# define HAVE_VSNPRINTF" +$ write aconf "#endif" +$ close aconf_in +$ close aconf +$ if f$search("''th'") .nes. "" then delete 'th';* +$! Build the thing plain or with mms +$! +$ write sys$output "Compiling Zlib sources ..." +$ if make.eqs."" +$ then +$ if (f$search( "example.obj;*") .nes. "") then delete example.obj;* +$ if (f$search( "minigzip.obj;*") .nes. "") then delete minigzip.obj;* +$ CALL MAKE adler32.OBJ "CC ''CCOPT' adler32" - + adler32.c zlib.h zconf.h +$ CALL MAKE compress.OBJ "CC ''CCOPT' compress" - + compress.c zlib.h zconf.h +$ CALL MAKE crc32.OBJ "CC ''CCOPT' crc32" - + crc32.c zlib.h zconf.h +$ CALL MAKE deflate.OBJ "CC ''CCOPT' deflate" - + deflate.c deflate.h zutil.h zlib.h zconf.h +$ CALL MAKE gzclose.OBJ "CC ''CCOPT' gzclose" - + gzclose.c zutil.h zlib.h zconf.h +$ CALL MAKE gzlib.OBJ "CC ''CCOPT' gzlib" - + gzlib.c zutil.h zlib.h zconf.h +$ CALL MAKE gzread.OBJ "CC ''CCOPT' gzread" - + gzread.c zutil.h zlib.h zconf.h +$ CALL MAKE gzwrite.OBJ "CC ''CCOPT' gzwrite" - + gzwrite.c zutil.h zlib.h zconf.h +$ CALL MAKE infback.OBJ "CC ''CCOPT' infback" - + infback.c zutil.h inftrees.h inflate.h inffast.h inffixed.h +$ CALL MAKE inffast.OBJ "CC ''CCOPT' inffast" - + inffast.c zutil.h zlib.h zconf.h inffast.h +$ CALL MAKE inflate.OBJ "CC ''CCOPT' inflate" - + inflate.c zutil.h zlib.h zconf.h infblock.h +$ CALL MAKE inftrees.OBJ "CC ''CCOPT' inftrees" - + inftrees.c zutil.h zlib.h zconf.h inftrees.h +$ CALL MAKE trees.OBJ "CC ''CCOPT' trees" - + trees.c deflate.h zutil.h zlib.h zconf.h +$ CALL MAKE uncompr.OBJ "CC ''CCOPT' uncompr" - + uncompr.c zlib.h zconf.h +$ CALL MAKE zutil.OBJ "CC ''CCOPT' zutil" - + zutil.c zutil.h zlib.h zconf.h +$ write sys$output "Building Zlib ..." +$ CALL MAKE libz.OLB "lib/crea libz.olb *.obj" *.OBJ +$ write sys$output "Building example..." +$ CALL MAKE example.OBJ "CC ''CCOPT' [.test]example" - + [.test]example.c zlib.h zconf.h +$ call make example.exe "LINK example,libz.olb/lib" example.obj libz.olb +$ write sys$output "Building minigzip..." +$ CALL MAKE minigzip.OBJ "CC ''CCOPT' [.test]minigzip" - + [.test]minigzip.c zlib.h zconf.h +$ call make minigzip.exe - + "LINK minigzip,libz.olb/lib" - + minigzip.obj libz.olb +$ else +$ gosub crea_mms +$ write sys$output "Make ''name' ''version' with ''Make' " +$ 'make' +$ endif +$! +$! Create shareable image +$! +$ gosub crea_olist +$ write sys$output "Creating libzshr.exe" +$ call map_2_shopt 'mapfile' 'optfile' +$ LINK_'lopts'/SHARE=libzshr.exe modules.opt/opt,'optfile'/opt +$ write sys$output "Zlib build completed" +$ delete/nolog tmp.opt;* +$ exit +$AMISS_ERR: +$ write sys$output "No source for config.hin found." +$ write sys$output "Tried any of ''aconf_in_file'" +$ goto err_exit +$CC_ERR: +$ write sys$output "C compiler required to build ''name'" +$ goto err_exit +$ERR_EXIT: +$ set message/facil/ident/sever/text +$ close/nolog optf +$ close/nolog topt +$ close/nolog aconf_in +$ close/nolog aconf +$ close/nolog out +$ close/nolog min +$ close/nolog mod +$ close/nolog h_in +$ write sys$output "Exiting..." +$ exit 2 +$! +$! +$MAKE: SUBROUTINE !SUBROUTINE TO CHECK DEPENDENCIES +$ V = 'F$Verify(0) +$! P1 = What we are trying to make +$! P2 = Command to make it +$! P3 - P8 What it depends on +$ +$ If F$Search(P1) .Eqs. "" Then Goto Makeit +$ Time = F$CvTime(F$File(P1,"RDT")) +$arg=3 +$Loop: +$ Argument = P'arg +$ If Argument .Eqs. "" Then Goto Exit +$ El=0 +$Loop2: +$ File = F$Element(El," ",Argument) +$ If File .Eqs. " " Then Goto Endl +$ AFile = "" +$Loop3: +$ OFile = AFile +$ AFile = F$Search(File) +$ If AFile .Eqs. "" .Or. AFile .Eqs. OFile Then Goto NextEl +$ If F$CvTime(F$File(AFile,"RDT")) .Ges. Time Then Goto Makeit +$ Goto Loop3 +$NextEL: +$ El = El + 1 +$ Goto Loop2 +$EndL: +$ arg=arg+1 +$ If arg .Le. 8 Then Goto Loop +$ Goto Exit +$ +$Makeit: +$ VV=F$VERIFY(0) +$ write sys$output P2 +$ 'P2 +$ VV='F$Verify(VV) +$Exit: +$ If V Then Set Verify +$ENDSUBROUTINE +$!------------------------------------------------------------------------------ +$! +$! Check command line options and set symbols accordingly +$! +$!------------------------------------------------------------------------------ +$! Version history +$! 0.01 20041206 First version to receive a number +$! 0.02 20060126 Add new "HELP" target +$ CHECK_OPTS: +$ i = 1 +$ OPT_LOOP: +$ if i .lt. 9 +$ then +$ cparm = f$edit(p'i',"upcase") +$! +$! Check if parameter actually contains something +$! +$ if f$edit(cparm,"trim") .nes. "" +$ then +$ if cparm .eqs. "DEBUG" +$ then +$ ccopt = ccopt + "/noopt/deb" +$ lopts = lopts + "/deb" +$ endif +$ if f$locate("CCOPT=",cparm) .lt. f$length(cparm) +$ then +$ start = f$locate("=",cparm) + 1 +$ len = f$length(cparm) - start +$ ccopt = ccopt + f$extract(start,len,cparm) +$ if f$locate("AS_IS",f$edit(ccopt,"UPCASE")) .lt. f$length(ccopt) - + then s_case = true +$ endif +$ if cparm .eqs. "LINK" then linkonly = true +$ if f$locate("LOPTS=",cparm) .lt. f$length(cparm) +$ then +$ start = f$locate("=",cparm) + 1 +$ len = f$length(cparm) - start +$ lopts = lopts + f$extract(start,len,cparm) +$ endif +$ if f$locate("CC=",cparm) .lt. f$length(cparm) +$ then +$ start = f$locate("=",cparm) + 1 +$ len = f$length(cparm) - start +$ cc_com = f$extract(start,len,cparm) + if (cc_com .nes. "DECC") .and. - + (cc_com .nes. "VAXC") .and. - + (cc_com .nes. "GNUC") +$ then +$ write sys$output "Unsupported compiler choice ''cc_com' ignored" +$ write sys$output "Use DECC, VAXC, or GNUC instead" +$ else +$ if cc_com .eqs. "DECC" then its_decc = true +$ if cc_com .eqs. "VAXC" then its_vaxc = true +$ if cc_com .eqs. "GNUC" then its_gnuc = true +$ endif +$ endif +$ if f$locate("MAKE=",cparm) .lt. f$length(cparm) +$ then +$ start = f$locate("=",cparm) + 1 +$ len = f$length(cparm) - start +$ mmks = f$extract(start,len,cparm) +$ if (mmks .eqs. "MMK") .or. (mmks .eqs. "MMS") +$ then +$ make = mmks +$ else +$ write sys$output "Unsupported make choice ''mmks' ignored" +$ write sys$output "Use MMK or MMS instead" +$ endif +$ endif +$ if cparm .eqs. "HELP" then gosub bhelp +$ endif +$ i = i + 1 +$ goto opt_loop +$ endif +$ return +$!------------------------------------------------------------------------------ +$! +$! Look for the compiler used +$! +$! Version history +$! 0.01 20040223 First version to receive a number +$! 0.02 20040229 Save/set value of decc$no_rooted_search_lists +$! 0.03 20060202 Extend handling of GNU C +$! 0.04 20090402 Compaq -> hp +$CHECK_COMPILER: +$ if (.not. (its_decc .or. its_vaxc .or. its_gnuc)) +$ then +$ its_decc = (f$search("SYS$SYSTEM:DECC$COMPILER.EXE") .nes. "") +$ its_vaxc = .not. its_decc .and. (F$Search("SYS$System:VAXC.Exe") .nes. "") +$ its_gnuc = .not. (its_decc .or. its_vaxc) .and. (f$trnlnm("gnu_cc") .nes. "") +$ endif +$! +$! Exit if no compiler available +$! +$ if (.not. (its_decc .or. its_vaxc .or. its_gnuc)) +$ then goto CC_ERR +$ else +$ if its_decc +$ then +$ write sys$output "CC compiler check ... hp C" +$ if f$trnlnm("decc$no_rooted_search_lists") .nes. "" +$ then +$ dnrsl = f$trnlnm("decc$no_rooted_search_lists") +$ endif +$ define/nolog decc$no_rooted_search_lists 1 +$ else +$ if its_vaxc then write sys$output "CC compiler check ... VAX C" +$ if its_gnuc +$ then +$ write sys$output "CC compiler check ... GNU C" +$ if f$trnlnm(topt) then write topt "gnu_cc:[000000]gcclib.olb/lib" +$ if f$trnlnm(optf) then write optf "gnu_cc:[000000]gcclib.olb/lib" +$ cc = "gcc" +$ endif +$ if f$trnlnm(topt) then write topt "sys$share:vaxcrtl.exe/share" +$ if f$trnlnm(optf) then write optf "sys$share:vaxcrtl.exe/share" +$ endif +$ endif +$ return +$!------------------------------------------------------------------------------ +$! +$! If MMS/MMK are available dump out the descrip.mms if required +$! +$CREA_MMS: +$ write sys$output "Creating descrip.mms..." +$ create descrip.mms +$ open/append out descrip.mms +$ copy sys$input: out +$ deck +# descrip.mms: MMS description file for building zlib on VMS +# written by Martin P.J. Zinser +# + +OBJS = adler32.obj, compress.obj, crc32.obj, gzclose.obj, gzlib.obj\ + gzread.obj, gzwrite.obj, uncompr.obj, infback.obj\ + deflate.obj, trees.obj, zutil.obj, inflate.obj, \ + inftrees.obj, inffast.obj + +$ eod +$ write out "CFLAGS=", ccopt +$ write out "LOPTS=", lopts +$ write out "all : example.exe minigzip.exe libz.olb" +$ copy sys$input: out +$ deck + @ write sys$output " Example applications available" + +libz.olb : libz.olb($(OBJS)) + @ write sys$output " libz available" + +example.exe : example.obj libz.olb + link $(LOPTS) example,libz.olb/lib + +minigzip.exe : minigzip.obj libz.olb + link $(LOPTS) minigzip,libz.olb/lib + +clean : + delete *.obj;*,libz.olb;*,*.opt;*,*.exe;* + + +# Other dependencies. +adler32.obj : adler32.c zutil.h zlib.h zconf.h +compress.obj : compress.c zlib.h zconf.h +crc32.obj : crc32.c zutil.h zlib.h zconf.h +deflate.obj : deflate.c deflate.h zutil.h zlib.h zconf.h +example.obj : [.test]example.c zlib.h zconf.h +gzclose.obj : gzclose.c zutil.h zlib.h zconf.h +gzlib.obj : gzlib.c zutil.h zlib.h zconf.h +gzread.obj : gzread.c zutil.h zlib.h zconf.h +gzwrite.obj : gzwrite.c zutil.h zlib.h zconf.h +inffast.obj : inffast.c zutil.h zlib.h zconf.h inftrees.h inffast.h +inflate.obj : inflate.c zutil.h zlib.h zconf.h +inftrees.obj : inftrees.c zutil.h zlib.h zconf.h inftrees.h +minigzip.obj : [.test]minigzip.c zlib.h zconf.h +trees.obj : trees.c deflate.h zutil.h zlib.h zconf.h +uncompr.obj : uncompr.c zlib.h zconf.h +zutil.obj : zutil.c zutil.h zlib.h zconf.h +infback.obj : infback.c zutil.h inftrees.h inflate.h inffast.h inffixed.h +$ eod +$ close out +$ return +$!------------------------------------------------------------------------------ +$! +$! Read list of core library sources from makefile.in and create options +$! needed to build shareable image +$! +$CREA_OLIST: +$ open/read min makefile.in +$ open/write mod modules.opt +$ src_check_list = "OBJZ =#OBJG =" +$MRLOOP: +$ read/end=mrdone min rec +$ i = 0 +$SRC_CHECK_LOOP: +$ src_check = f$element(i, "#", src_check_list) +$ i = i+1 +$ if src_check .eqs. "#" then goto mrloop +$ if (f$extract(0,6,rec) .nes. src_check) then goto src_check_loop +$ rec = rec - src_check +$ gosub extra_filnam +$ if (f$element(1,"\",rec) .eqs. "\") then goto mrloop +$MRSLOOP: +$ read/end=mrdone min rec +$ gosub extra_filnam +$ if (f$element(1,"\",rec) .nes. "\") then goto mrsloop +$MRDONE: +$ close min +$ close mod +$ return +$!------------------------------------------------------------------------------ +$! +$! Take record extracted in crea_olist and split it into single filenames +$! +$EXTRA_FILNAM: +$ myrec = f$edit(rec - "\", "trim,compress") +$ i = 0 +$FELOOP: +$ srcfil = f$element(i," ", myrec) +$ if (srcfil .nes. " ") +$ then +$ write mod f$parse(srcfil,,,"NAME"), ".obj" +$ i = i + 1 +$ goto feloop +$ endif +$ return +$!------------------------------------------------------------------------------ +$! +$! Find current Zlib version number +$! +$FIND_VERSION: +$ open/read h_in 'v_file' +$hloop: +$ read/end=hdone h_in rec +$ rec = f$edit(rec,"TRIM") +$ if (f$extract(0,1,rec) .nes. "#") then goto hloop +$ rec = f$edit(rec - "#", "TRIM") +$ if f$element(0," ",rec) .nes. "define" then goto hloop +$ if f$element(1," ",rec) .eqs. v_string +$ then +$ version = 'f$element(2," ",rec)' +$ goto hdone +$ endif +$ goto hloop +$hdone: +$ close h_in +$ return +$!------------------------------------------------------------------------------ +$! +$CHECK_CONFIG: +$! +$ in_ldef = f$locate(cdef,libdefs) +$ if (in_ldef .lt. f$length(libdefs)) +$ then +$ write aconf "#define ''cdef' 1" +$ libdefs = f$extract(0,in_ldef,libdefs) + - + f$extract(in_ldef + f$length(cdef) + 1, - + f$length(libdefs) - in_ldef - f$length(cdef) - 1, - + libdefs) +$ else +$ if (f$type('cdef') .eqs. "INTEGER") +$ then +$ write aconf "#define ''cdef' ", 'cdef' +$ else +$ if (f$type('cdef') .eqs. "STRING") +$ then +$ write aconf "#define ''cdef' ", """", '''cdef'', """" +$ else +$ gosub check_cc_def +$ endif +$ endif +$ endif +$ return +$!------------------------------------------------------------------------------ +$! +$! Check if this is a define relating to the properties of the C/C++ +$! compiler +$! +$ CHECK_CC_DEF: +$ if (cdef .eqs. "_LARGEFILE64_SOURCE") +$ then +$ copy sys$input: 'tc' +$ deck +#include "tconfig" +#define _LARGEFILE +#include + +int main(){ +FILE *fp; + fp = fopen("temp.txt","r"); + fseeko(fp,1,SEEK_SET); + fclose(fp); +} + +$ eod +$ test_inv = false +$ comm_h = false +$ gosub cc_prop_check +$ return +$ endif +$ write aconf "/* ", line, " */" +$ return +$!------------------------------------------------------------------------------ +$! +$! Check for properties of C/C++ compiler +$! +$! Version history +$! 0.01 20031020 First version to receive a number +$! 0.02 20031022 Added logic for defines with value +$! 0.03 20040309 Make sure local config file gets not deleted +$! 0.04 20041230 Also write include for configure run +$! 0.05 20050103 Add processing of "comment defines" +$CC_PROP_CHECK: +$ cc_prop = true +$ is_need = false +$ is_need = (f$extract(0,4,cdef) .eqs. "NEED") .or. (test_inv .eq. true) +$ if f$search(th) .eqs. "" then create 'th' +$ set message/nofac/noident/nosever/notext +$ on error then continue +$ cc 'tmpnam' +$ if .not. ($status) then cc_prop = false +$ on error then continue +$! The headers might lie about the capabilities of the RTL +$ link 'tmpnam',tmp.opt/opt +$ if .not. ($status) then cc_prop = false +$ set message/fac/ident/sever/text +$ on error then goto err_exit +$ delete/nolog 'tmpnam'.*;*/exclude='th' +$ if (cc_prop .and. .not. is_need) .or. - + (.not. cc_prop .and. is_need) +$ then +$ write sys$output "Checking for ''cdef'... yes" +$ if f$type('cdef_val'_yes) .nes. "" +$ then +$ if f$type('cdef_val'_yes) .eqs. "INTEGER" - + then call write_config f$fao("#define !AS !UL",cdef,'cdef_val'_yes) +$ if f$type('cdef_val'_yes) .eqs. "STRING" - + then call write_config f$fao("#define !AS !AS",cdef,'cdef_val'_yes) +$ else +$ call write_config f$fao("#define !AS 1",cdef) +$ endif +$ if (cdef .eqs. "HAVE_FSEEKO") .or. (cdef .eqs. "_LARGE_FILES") .or. - + (cdef .eqs. "_LARGEFILE64_SOURCE") then - + call write_config f$string("#define _LARGEFILE 1") +$ else +$ write sys$output "Checking for ''cdef'... no" +$ if (comm_h) +$ then + call write_config f$fao("/* !AS */",line) +$ else +$ if f$type('cdef_val'_no) .nes. "" +$ then +$ if f$type('cdef_val'_no) .eqs. "INTEGER" - + then call write_config f$fao("#define !AS !UL",cdef,'cdef_val'_no) +$ if f$type('cdef_val'_no) .eqs. "STRING" - + then call write_config f$fao("#define !AS !AS",cdef,'cdef_val'_no) +$ else +$ call write_config f$fao("#undef !AS",cdef) +$ endif +$ endif +$ endif +$ return +$!------------------------------------------------------------------------------ +$! +$! Check for properties of C/C++ compiler with multiple result values +$! +$! Version history +$! 0.01 20040127 First version +$! 0.02 20050103 Reconcile changes from cc_prop up to version 0.05 +$CC_MPROP_CHECK: +$ cc_prop = true +$ i = 1 +$ idel = 1 +$ MT_LOOP: +$ if f$type(result_'i') .eqs. "STRING" +$ then +$ set message/nofac/noident/nosever/notext +$ on error then continue +$ cc 'tmpnam'_'i' +$ if .not. ($status) then cc_prop = false +$ on error then continue +$! The headers might lie about the capabilities of the RTL +$ link 'tmpnam'_'i',tmp.opt/opt +$ if .not. ($status) then cc_prop = false +$ set message/fac/ident/sever/text +$ on error then goto err_exit +$ delete/nolog 'tmpnam'_'i'.*;* +$ if (cc_prop) +$ then +$ write sys$output "Checking for ''cdef'... ", mdef_'i' +$ if f$type(mdef_'i') .eqs. "INTEGER" - + then call write_config f$fao("#define !AS !UL",cdef,mdef_'i') +$ if f$type('cdef_val'_yes) .eqs. "STRING" - + then call write_config f$fao("#define !AS !AS",cdef,mdef_'i') +$ goto msym_clean +$ else +$ i = i + 1 +$ goto mt_loop +$ endif +$ endif +$ write sys$output "Checking for ''cdef'... no" +$ call write_config f$fao("#undef !AS",cdef) +$ MSYM_CLEAN: +$ if (idel .le. msym_max) +$ then +$ delete/sym mdef_'idel' +$ idel = idel + 1 +$ goto msym_clean +$ endif +$ return +$!------------------------------------------------------------------------------ +$! +$! Write configuration to both permanent and temporary config file +$! +$! Version history +$! 0.01 20031029 First version to receive a number +$! +$WRITE_CONFIG: SUBROUTINE +$ write aconf 'p1' +$ open/append confh 'th' +$ write confh 'p1' +$ close confh +$ENDSUBROUTINE +$!------------------------------------------------------------------------------ +$! +$! Analyze the project map file and create the symbol vector for a shareable +$! image from it +$! +$! Version history +$! 0.01 20120128 First version +$! 0.02 20120226 Add pre-load logic +$! +$ MAP_2_SHOPT: Subroutine +$! +$ SAY := "WRITE_ SYS$OUTPUT" +$! +$ IF F$SEARCH("''P1'") .EQS. "" +$ THEN +$ SAY "MAP_2_SHOPT-E-NOSUCHFILE: Error, inputfile ''p1' not available" +$ goto exit_m2s +$ ENDIF +$ IF "''P2'" .EQS. "" +$ THEN +$ SAY "MAP_2_SHOPT: Error, no output file provided" +$ goto exit_m2s +$ ENDIF +$! +$ module1 = "deflate#deflateEnd#deflateInit_#deflateParams#deflateSetDictionary" +$ module2 = "gzclose#gzerror#gzgetc#gzgets#gzopen#gzprintf#gzputc#gzputs#gzread" +$ module3 = "gzseek#gztell#inflate#inflateEnd#inflateInit_#inflateSetDictionary" +$ module4 = "inflateSync#uncompress#zlibVersion#compress" +$ open/read map 'p1 +$ if axp .or. ia64 +$ then +$ open/write aopt a.opt +$ open/write bopt b.opt +$ write aopt " CASE_SENSITIVE=YES" +$ write bopt "SYMBOL_VECTOR= (-" +$ mod_sym_num = 1 +$ MOD_SYM_LOOP: +$ if f$type(module'mod_sym_num') .nes. "" +$ then +$ mod_in = 0 +$ MOD_SYM_IN: +$ shared_proc = f$element(mod_in, "#", module'mod_sym_num') +$ if shared_proc .nes. "#" +$ then +$ write aopt f$fao(" symbol_vector=(!AS/!AS=PROCEDURE)",- + f$edit(shared_proc,"upcase"),shared_proc) +$ write bopt f$fao("!AS=PROCEDURE,-",shared_proc) +$ mod_in = mod_in + 1 +$ goto mod_sym_in +$ endif +$ mod_sym_num = mod_sym_num + 1 +$ goto mod_sym_loop +$ endif +$MAP_LOOP: +$ read/end=map_end map line +$ if (f$locate("{",line).lt. f$length(line)) .or. - + (f$locate("global:", line) .lt. f$length(line)) +$ then +$ proc = true +$ goto map_loop +$ endif +$ if f$locate("}",line).lt. f$length(line) then proc = false +$ if f$locate("local:", line) .lt. f$length(line) then proc = false +$ if proc +$ then +$ shared_proc = f$edit(line,"collapse") +$ chop_semi = f$locate(";", shared_proc) +$ if chop_semi .lt. f$length(shared_proc) then - + shared_proc = f$extract(0, chop_semi, shared_proc) +$ write aopt f$fao(" symbol_vector=(!AS/!AS=PROCEDURE)",- + f$edit(shared_proc,"upcase"),shared_proc) +$ write bopt f$fao("!AS=PROCEDURE,-",shared_proc) +$ endif +$ goto map_loop +$MAP_END: +$ close/nolog aopt +$ close/nolog bopt +$ open/append libopt 'p2' +$ open/read aopt a.opt +$ open/read bopt b.opt +$ALOOP: +$ read/end=aloop_end aopt line +$ write libopt line +$ goto aloop +$ALOOP_END: +$ close/nolog aopt +$ sv = "" +$BLOOP: +$ read/end=bloop_end bopt svn +$ if (svn.nes."") +$ then +$ if (sv.nes."") then write libopt sv +$ sv = svn +$ endif +$ goto bloop +$BLOOP_END: +$ write libopt f$extract(0,f$length(sv)-2,sv), "-" +$ write libopt ")" +$ close/nolog bopt +$ delete/nolog/noconf a.opt;*,b.opt;* +$ else +$ if vax +$ then +$ open/append libopt 'p2' +$ mod_sym_num = 1 +$ VMOD_SYM_LOOP: +$ if f$type(module'mod_sym_num') .nes. "" +$ then +$ mod_in = 0 +$ VMOD_SYM_IN: +$ shared_proc = f$element(mod_in, "#", module'mod_sym_num') +$ if shared_proc .nes. "#" +$ then +$ write libopt f$fao("UNIVERSAL=!AS",- + f$edit(shared_proc,"upcase")) +$ mod_in = mod_in + 1 +$ goto vmod_sym_in +$ endif +$ mod_sym_num = mod_sym_num + 1 +$ goto vmod_sym_loop +$ endif +$VMAP_LOOP: +$ read/end=vmap_end map line +$ if (f$locate("{",line).lt. f$length(line)) .or. - + (f$locate("global:", line) .lt. f$length(line)) +$ then +$ proc = true +$ goto vmap_loop +$ endif +$ if f$locate("}",line).lt. f$length(line) then proc = false +$ if f$locate("local:", line) .lt. f$length(line) then proc = false +$ if proc +$ then +$ shared_proc = f$edit(line,"collapse") +$ chop_semi = f$locate(";", shared_proc) +$ if chop_semi .lt. f$length(shared_proc) then - + shared_proc = f$extract(0, chop_semi, shared_proc) +$ write libopt f$fao("UNIVERSAL=!AS",- + f$edit(shared_proc,"upcase")) +$ endif +$ goto vmap_loop +$VMAP_END: +$ else +$ write sys$output "Unknown Architecture (Not VAX, AXP, or IA64)" +$ write sys$output "No options file created" +$ endif +$ endif +$ EXIT_M2S: +$ close/nolog map +$ close/nolog libopt +$ endsubroutine diff --git a/contrib/zlib/treebuild.xml b/contrib/zlib/treebuild.xml new file mode 100644 index 000000000..6bd6677c4 --- /dev/null +++ b/contrib/zlib/treebuild.xml @@ -0,0 +1,116 @@ + + + + zip compression library + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/zlib/trees.c b/contrib/zlib/trees.c index 1fd7759ef..50cf4b457 100644 --- a/contrib/zlib/trees.c +++ b/contrib/zlib/trees.c @@ -1,5 +1,5 @@ /* trees.c -- output deflated data using Huffman coding - * Copyright (C) 1995-2012 Jean-loup Gailly + * Copyright (C) 1995-2017 Jean-loup Gailly * detect_data_type() function provided freely by Cosmin Truta, 2006 * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -36,7 +36,7 @@ #include "deflate.h" -#ifdef DEBUG +#ifdef ZLIB_DEBUG # include #endif @@ -122,13 +122,13 @@ struct static_tree_desc_s { int max_length; /* max bit length for the codes */ }; -local static_tree_desc static_l_desc = +local const static_tree_desc static_l_desc = {static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; -local static_tree_desc static_d_desc = +local const static_tree_desc static_d_desc = {static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; -local static_tree_desc static_bl_desc = +local const static_tree_desc static_bl_desc = {(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; /* =========================================================================== @@ -152,18 +152,16 @@ local int detect_data_type OF((deflate_state *s)); local unsigned bi_reverse OF((unsigned value, int length)); local void bi_windup OF((deflate_state *s)); local void bi_flush OF((deflate_state *s)); -local void copy_block OF((deflate_state *s, charf *buf, unsigned len, - int header)); #ifdef GEN_TREES_H local void gen_trees_header OF((void)); #endif -#ifndef DEBUG +#ifndef ZLIB_DEBUG # define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) /* Send a code of the given tree. c and tree must not have side effects */ -#else /* DEBUG */ +#else /* !ZLIB_DEBUG */ # define send_code(s, c, tree) \ { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ send_bits(s, tree[c].Code, tree[c].Len); } @@ -182,7 +180,7 @@ local void gen_trees_header OF((void)); * Send a value on a given number of bits. * IN assertion: length <= 16 and value fits in length bits. */ -#ifdef DEBUG +#ifdef ZLIB_DEBUG local void send_bits OF((deflate_state *s, int value, int length)); local void send_bits(s, value, length) @@ -208,12 +206,12 @@ local void send_bits(s, value, length) s->bi_valid += length; } } -#else /* !DEBUG */ +#else /* !ZLIB_DEBUG */ #define send_bits(s, value, length) \ { int len = length;\ if (s->bi_valid > (int)Buf_size - len) {\ - int val = value;\ + int val = (int)value;\ s->bi_buf |= (ush)val << s->bi_valid;\ put_short(s, s->bi_buf);\ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ @@ -223,7 +221,7 @@ local void send_bits(s, value, length) s->bi_valid += len;\ }\ } -#endif /* DEBUG */ +#endif /* ZLIB_DEBUG */ /* the arguments must not have side effects */ @@ -317,7 +315,7 @@ local void tr_static_init() * Genererate the file trees.h describing the static trees. */ #ifdef GEN_TREES_H -# ifndef DEBUG +# ifndef ZLIB_DEBUG # include # endif @@ -394,7 +392,7 @@ void ZLIB_INTERNAL _tr_init(s) s->bi_buf = 0; s->bi_valid = 0; -#ifdef DEBUG +#ifdef ZLIB_DEBUG s->compressed_len = 0L; s->bits_sent = 0L; #endif @@ -522,12 +520,12 @@ local void gen_bitlen(s, desc) xbits = 0; if (n >= base) xbits = extra[n-base]; f = tree[n].Freq; - s->opt_len += (ulg)f * (bits + xbits); - if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + s->opt_len += (ulg)f * (unsigned)(bits + xbits); + if (stree) s->static_len += (ulg)f * (unsigned)(stree[n].Len + xbits); } if (overflow == 0) return; - Trace((stderr,"\nbit length overflow\n")); + Tracev((stderr,"\nbit length overflow\n")); /* This happens for example on obj2 and pic of the Calgary corpus */ /* Find the first bit length which could increase: */ @@ -554,9 +552,8 @@ local void gen_bitlen(s, desc) m = s->heap[--h]; if (m > max_code) continue; if ((unsigned) tree[m].Len != (unsigned) bits) { - Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); - s->opt_len += ((long)bits - (long)tree[m].Len) - *(long)tree[m].Freq; + Tracev((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((ulg)bits - tree[m].Len) * tree[m].Freq; tree[m].Len = (ush)bits; } n--; @@ -578,7 +575,7 @@ local void gen_codes (tree, max_code, bl_count) ushf *bl_count; /* number of codes at each bit length */ { ush next_code[MAX_BITS+1]; /* next code value for each bit length */ - ush code = 0; /* running code value */ + unsigned code = 0; /* running code value */ int bits; /* bit index */ int n; /* code index */ @@ -586,7 +583,8 @@ local void gen_codes (tree, max_code, bl_count) * without bit reversal. */ for (bits = 1; bits <= MAX_BITS; bits++) { - next_code[bits] = code = (code + bl_count[bits-1]) << 1; + code = (code + bl_count[bits-1]) << 1; + next_code[bits] = (ush)code; } /* Check that the bit counts in bl_count are consistent. The last code * must be all ones. @@ -599,7 +597,7 @@ local void gen_codes (tree, max_code, bl_count) int len = tree[n].Len; if (len == 0) continue; /* Now reverse the bits */ - tree[n].Code = bi_reverse(next_code[len]++, len); + tree[n].Code = (ush)bi_reverse(next_code[len]++, len); Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ", n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1)); @@ -821,7 +819,7 @@ local int build_bl_tree(s) if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; } /* Update opt_len to include the bit length tree and counts */ - s->opt_len += 3*(max_blindex+1) + 5+5+4; + s->opt_len += 3*((ulg)max_blindex+1) + 5+5+4; Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", s->opt_len, s->static_len)); @@ -869,11 +867,17 @@ void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) int last; /* one if this is the last block for a file */ { send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */ -#ifdef DEBUG + bi_windup(s); /* align on byte boundary */ + put_short(s, (ush)stored_len); + put_short(s, (ush)~stored_len); + zmemcpy(s->pending_buf + s->pending, (Bytef *)buf, stored_len); + s->pending += stored_len; +#ifdef ZLIB_DEBUG s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; s->compressed_len += (stored_len + 4) << 3; + s->bits_sent += 2*16; + s->bits_sent += stored_len<<3; #endif - copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ } /* =========================================================================== @@ -894,7 +898,7 @@ void ZLIB_INTERNAL _tr_align(s) { send_bits(s, STATIC_TREES<<1, 3); send_code(s, END_BLOCK, static_ltree); -#ifdef DEBUG +#ifdef ZLIB_DEBUG s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ #endif bi_flush(s); @@ -902,7 +906,7 @@ void ZLIB_INTERNAL _tr_align(s) /* =========================================================================== * Determine the best encoding for the current block: dynamic trees, static - * trees or store, and output the encoded block to the zip file. + * trees or store, and write out the encoded block. */ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) deflate_state *s; @@ -974,7 +978,7 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) send_bits(s, (STATIC_TREES<<1)+last, 3); compress_block(s, (const ct_data *)static_ltree, (const ct_data *)static_dtree); -#ifdef DEBUG +#ifdef ZLIB_DEBUG s->compressed_len += 3 + s->static_len; #endif } else { @@ -983,7 +987,7 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) max_blindex+1); compress_block(s, (const ct_data *)s->dyn_ltree, (const ct_data *)s->dyn_dtree); -#ifdef DEBUG +#ifdef ZLIB_DEBUG s->compressed_len += 3 + s->opt_len; #endif } @@ -995,7 +999,7 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) if (last) { bi_windup(s); -#ifdef DEBUG +#ifdef ZLIB_DEBUG s->compressed_len += 7; /* align on byte boundary */ #endif } @@ -1090,7 +1094,7 @@ local void compress_block(s, ltree, dtree) send_code(s, code, dtree); /* send the distance code */ extra = extra_dbits[code]; if (extra != 0) { - dist -= base_dist[code]; + dist -= (unsigned)base_dist[code]; send_bits(s, dist, extra); /* send the extra distance bits */ } } /* literal or match pair ? */ @@ -1193,34 +1197,7 @@ local void bi_windup(s) } s->bi_buf = 0; s->bi_valid = 0; -#ifdef DEBUG +#ifdef ZLIB_DEBUG s->bits_sent = (s->bits_sent+7) & ~7; #endif } - -/* =========================================================================== - * Copy a stored block, storing first the length and its - * one's complement if requested. - */ -local void copy_block(s, buf, len, header) - deflate_state *s; - charf *buf; /* the input data */ - unsigned len; /* its length */ - int header; /* true if block header must be written */ -{ - bi_windup(s); /* align on byte boundary */ - - if (header) { - put_short(s, (ush)len); - put_short(s, (ush)~len); -#ifdef DEBUG - s->bits_sent += 2*16; -#endif - } -#ifdef DEBUG - s->bits_sent += (ulg)len<<3; -#endif - while (len--) { - put_byte(s, *buf++); - } -} diff --git a/contrib/zlib/uncompr.c b/contrib/zlib/uncompr.c index 242e9493d..f03a1a865 100644 --- a/contrib/zlib/uncompr.c +++ b/contrib/zlib/uncompr.c @@ -1,5 +1,5 @@ /* uncompr.c -- decompress a memory buffer - * Copyright (C) 1995-2003, 2010 Jean-loup Gailly. + * Copyright (C) 1995-2003, 2010, 2014, 2016 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -9,51 +9,85 @@ #include "zlib.h" /* =========================================================================== - Decompresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total - size of the destination buffer, which must be large enough to hold the - entire uncompressed data. (The size of the uncompressed data must have - been saved previously by the compressor and transmitted to the decompressor - by some mechanism outside the scope of this compression library.) - Upon exit, destLen is the actual size of the compressed buffer. + Decompresses the source buffer into the destination buffer. *sourceLen is + the byte length of the source buffer. Upon entry, *destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, + *destLen is the size of the decompressed data and *sourceLen is the number + of source bytes consumed. Upon return, source + *sourceLen points to the + first unused input byte. - uncompress returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer, or Z_DATA_ERROR if the input data was corrupted. + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, or + Z_DATA_ERROR if the input data was corrupted, including if the input data is + an incomplete zlib stream. */ +int ZEXPORT uncompress2 (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong *sourceLen; +{ + z_stream stream; + int err; + const uInt max = (uInt)-1; + uLong len, left; + Byte buf[1]; /* for detection of incomplete stream when *destLen == 0 */ + + len = *sourceLen; + if (*destLen) { + left = *destLen; + *destLen = 0; + } + else { + left = 1; + dest = buf; + } + + stream.next_in = (z_const Bytef *)source; + stream.avail_in = 0; + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + stream.next_out = dest; + stream.avail_out = 0; + + do { + if (stream.avail_out == 0) { + stream.avail_out = left > (uLong)max ? max : (uInt)left; + left -= stream.avail_out; + } + if (stream.avail_in == 0) { + stream.avail_in = len > (uLong)max ? max : (uInt)len; + len -= stream.avail_in; + } + err = inflate(&stream, Z_NO_FLUSH); + } while (err == Z_OK); + + *sourceLen -= len + stream.avail_in; + if (dest != buf) + *destLen = stream.total_out; + else if (stream.total_out && err == Z_BUF_ERROR) + left = 1; + + inflateEnd(&stream); + return err == Z_STREAM_END ? Z_OK : + err == Z_NEED_DICT ? Z_DATA_ERROR : + err == Z_BUF_ERROR && left + stream.avail_out ? Z_DATA_ERROR : + err; +} + int ZEXPORT uncompress (dest, destLen, source, sourceLen) Bytef *dest; uLongf *destLen; const Bytef *source; uLong sourceLen; { - z_stream stream; - int err; - - stream.next_in = (z_const Bytef *)source; - stream.avail_in = (uInt)sourceLen; - /* Check for source > 64K on 16-bit machine: */ - if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; - - stream.next_out = dest; - stream.avail_out = (uInt)*destLen; - if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; - - stream.zalloc = (alloc_func)0; - stream.zfree = (free_func)0; - - err = inflateInit(&stream); - if (err != Z_OK) return err; - - err = inflate(&stream, Z_FINISH); - if (err != Z_STREAM_END) { - inflateEnd(&stream); - if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) - return Z_DATA_ERROR; - return err; - } - *destLen = stream.total_out; - - err = inflateEnd(&stream); - return err; + return uncompress2(dest, destLen, source, &sourceLen); } diff --git a/contrib/zlib/win32/Makefile.gcc b/contrib/zlib/win32/Makefile.gcc index 6d1ded622..305be50af 100644 --- a/contrib/zlib/win32/Makefile.gcc +++ b/contrib/zlib/win32/Makefile.gcc @@ -39,7 +39,7 @@ IMPLIB = libz.dll.a SHARED_MODE=0 #LOC = -DASMV -#LOC = -DDEBUG -g +#LOC = -DZLIB_DEBUG -g PREFIX = CC = $(PREFIX)gcc diff --git a/contrib/zlib/win32/Makefile.msc b/contrib/zlib/win32/Makefile.msc index 67b773171..6831882de 100644 --- a/contrib/zlib/win32/Makefile.msc +++ b/contrib/zlib/win32/Makefile.msc @@ -1,5 +1,5 @@ # Makefile for zlib using Microsoft (Visual) C -# zlib is copyright (C) 1995-2006 Jean-loup Gailly and Mark Adler +# zlib is copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler # # Usage: # nmake -f win32/Makefile.msc (standard build) diff --git a/contrib/zlib/win32/README-WIN32.txt b/contrib/zlib/win32/README-WIN32.txt index 3d77d521e..a1de359a3 100644 --- a/contrib/zlib/win32/README-WIN32.txt +++ b/contrib/zlib/win32/README-WIN32.txt @@ -1,6 +1,6 @@ ZLIB DATA COMPRESSION LIBRARY -zlib 1.2.8 is a general purpose data compression library. All the code is +zlib 1.2.11.1 is a general purpose data compression library. All the code is thread safe. The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) @@ -22,7 +22,7 @@ before asking for help. Manifest: -The package zlib-1.2.8-win32-x86.zip will contain the following files: +The package zlib-1.2.11.1-win32-x86.zip will contain the following files: README-WIN32.txt This document ChangeLog Changes since previous zlib packages @@ -72,7 +72,7 @@ are too numerous to cite here. Copyright notice: - (C) 1995-2012 Jean-loup Gailly and Mark Adler + (C) 1995-2017 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/contrib/zlib/win32/VisualC.txt b/contrib/zlib/win32/VisualC.txt index 579a5fc9e..1005b2194 100644 --- a/contrib/zlib/win32/VisualC.txt +++ b/contrib/zlib/win32/VisualC.txt @@ -1,3 +1,3 @@ To build zlib using the Microsoft Visual C++ environment, -use the appropriate project from the projects/ directory. +use the appropriate project from the contrib/vstudio/ directory. diff --git a/contrib/zlib/win32/zlib.def b/contrib/zlib/win32/zlib.def index face65518..a2188b000 100644 --- a/contrib/zlib/win32/zlib.def +++ b/contrib/zlib/win32/zlib.def @@ -8,6 +8,7 @@ EXPORTS inflateEnd ; advanced functions deflateSetDictionary + deflateGetDictionary deflateCopy deflateReset deflateParams @@ -33,12 +34,15 @@ EXPORTS compress2 compressBound uncompress + uncompress2 gzopen gzdopen gzbuffer gzsetparams gzread + gzfread gzwrite + gzfwrite gzprintf gzvprintf gzputs @@ -67,7 +71,9 @@ EXPORTS crc32_combine64 ; checksum functions adler32 + adler32_z crc32 + crc32_z adler32_combine crc32_combine ; various hacks, don't look :) @@ -81,6 +87,8 @@ EXPORTS inflateSyncPoint get_crc_table inflateUndermine + inflateValidate + inflateCodesUsed inflateResetKeep deflateResetKeep gzopen_w diff --git a/contrib/zlib/win32/zlib1.rc b/contrib/zlib/win32/zlib1.rc index 5c0feed1b..234e641c3 100644 --- a/contrib/zlib/win32/zlib1.rc +++ b/contrib/zlib/win32/zlib1.rc @@ -26,7 +26,7 @@ BEGIN VALUE "FileDescription", "zlib data compression library\0" VALUE "FileVersion", ZLIB_VERSION "\0" VALUE "InternalName", "zlib1.dll\0" - VALUE "LegalCopyright", "(C) 1995-2013 Jean-loup Gailly & Mark Adler\0" + VALUE "LegalCopyright", "(C) 1995-2017 Jean-loup Gailly & Mark Adler\0" VALUE "OriginalFilename", "zlib1.dll\0" VALUE "ProductName", "zlib\0" VALUE "ProductVersion", ZLIB_VERSION "\0" diff --git a/contrib/zlib/zconf.h.cmakein b/contrib/zlib/zconf.h.cmakein index 043019cda..a7f24cce6 100644 --- a/contrib/zlib/zconf.h.cmakein +++ b/contrib/zlib/zconf.h.cmakein @@ -1,5 +1,5 @@ /* zconf.h -- configuration of the zlib compression library - * Copyright (C) 1995-2013 Jean-loup Gailly. + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -19,7 +19,7 @@ #ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ # define Z_PREFIX_SET -/* all linked symbols */ +/* all linked symbols and init macros */ # define _dist_code z__dist_code # define _length_code z__length_code # define _tr_align z__tr_align @@ -31,6 +31,7 @@ # define adler32 z_adler32 # define adler32_combine z_adler32_combine # define adler32_combine64 z_adler32_combine64 +# define adler32_z z_adler32_z # ifndef Z_SOLO # define compress z_compress # define compress2 z_compress2 @@ -39,10 +40,14 @@ # define crc32 z_crc32 # define crc32_combine z_crc32_combine # define crc32_combine64 z_crc32_combine64 +# define crc32_z z_crc32_z # define deflate z_deflate # define deflateBound z_deflateBound # define deflateCopy z_deflateCopy # define deflateEnd z_deflateEnd +# define deflateGetDictionary z_deflateGetDictionary +# define deflateInit z_deflateInit +# define deflateInit2 z_deflateInit2 # define deflateInit2_ z_deflateInit2_ # define deflateInit_ z_deflateInit_ # define deflateParams z_deflateParams @@ -69,6 +74,8 @@ # define gzeof z_gzeof # define gzerror z_gzerror # define gzflush z_gzflush +# define gzfread z_gzfread +# define gzfwrite z_gzfwrite # define gzgetc z_gzgetc # define gzgetc_ z_gzgetc_ # define gzgets z_gzgets @@ -80,7 +87,6 @@ # define gzopen_w z_gzopen_w # endif # define gzprintf z_gzprintf -# define gzvprintf z_gzvprintf # define gzputc z_gzputc # define gzputs z_gzputs # define gzread z_gzread @@ -91,32 +97,39 @@ # define gztell z_gztell # define gztell64 z_gztell64 # define gzungetc z_gzungetc +# define gzvprintf z_gzvprintf # define gzwrite z_gzwrite # endif # define inflate z_inflate # define inflateBack z_inflateBack # define inflateBackEnd z_inflateBackEnd +# define inflateBackInit z_inflateBackInit # define inflateBackInit_ z_inflateBackInit_ +# define inflateCodesUsed z_inflateCodesUsed # define inflateCopy z_inflateCopy # define inflateEnd z_inflateEnd +# define inflateGetDictionary z_inflateGetDictionary # define inflateGetHeader z_inflateGetHeader +# define inflateInit z_inflateInit +# define inflateInit2 z_inflateInit2 # define inflateInit2_ z_inflateInit2_ # define inflateInit_ z_inflateInit_ # define inflateMark z_inflateMark # define inflatePrime z_inflatePrime # define inflateReset z_inflateReset # define inflateReset2 z_inflateReset2 +# define inflateResetKeep z_inflateResetKeep # define inflateSetDictionary z_inflateSetDictionary -# define inflateGetDictionary z_inflateGetDictionary # define inflateSync z_inflateSync # define inflateSyncPoint z_inflateSyncPoint # define inflateUndermine z_inflateUndermine -# define inflateResetKeep z_inflateResetKeep +# define inflateValidate z_inflateValidate # define inflate_copyright z_inflate_copyright # define inflate_fast z_inflate_fast # define inflate_table z_inflate_table # ifndef Z_SOLO # define uncompress z_uncompress +# define uncompress2 z_uncompress2 # endif # define zError z_zError # ifndef Z_SOLO @@ -226,9 +239,19 @@ # define z_const #endif -/* Some Mac compilers merge all .h files incorrectly: */ -#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) -# define NO_DUMMY_DECL +#ifdef Z_SOLO + typedef unsigned long z_size_t; +#else +# define z_longlong long long +# if defined(NO_SIZE_T) + typedef unsigned NO_SIZE_T z_size_t; +# elif defined(STDC) +# include + typedef size_t z_size_t; +# else + typedef unsigned long z_size_t; +# endif +# undef z_longlong #endif /* Maximum value for memLevel in deflateInit2 */ @@ -258,7 +281,7 @@ Of course this will generally degrade compression (there's no free lunch). The memory requirements for inflate are (in bytes) 1 << windowBits - that is, 32K for windowBits=15 (default value) plus a few kilobytes + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes for small objects. */ diff --git a/contrib/zlib/zconf.in.h b/contrib/zlib/zconf.h.in similarity index 50% rename from contrib/zlib/zconf.in.h rename to contrib/zlib/zconf.h.in index 03a9431c8..5e1d68a00 100644 --- a/contrib/zlib/zconf.in.h +++ b/contrib/zlib/zconf.h.in @@ -1,5 +1,5 @@ /* zconf.h -- configuration of the zlib compression library - * Copyright (C) 1995-2005 Jean-loup Gailly. + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -11,52 +11,158 @@ /* * If you *really* need a unique prefix for all types and library functions, * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". */ -#ifdef Z_PREFIX -# define deflateInit_ z_deflateInit_ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols and init macros */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define adler32_z z_adler32_z +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define crc32_z z_crc32_z # define deflate z_deflate -# define deflateEnd z_deflateEnd -# define inflateInit_ z_inflateInit_ -# define inflate z_inflate -# define inflateEnd z_inflateEnd -# define deflateInit2_ z_deflateInit2_ -# define deflateSetDictionary z_deflateSetDictionary -# define deflateCopy z_deflateCopy -# define deflateReset z_deflateReset -# define deflateParams z_deflateParams # define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateGetDictionary z_deflateGetDictionary +# define deflateInit z_deflateInit +# define deflateInit2 z_deflateInit2 +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending # define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzfread z_gzfread +# define gzfwrite z_gzfwrite +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzvprintf z_gzvprintf +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit z_inflateBackInit +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCodesUsed z_inflateCodesUsed +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetDictionary z_inflateGetDictionary +# define inflateGetHeader z_inflateGetHeader +# define inflateInit z_inflateInit +# define inflateInit2 z_inflateInit2 # define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateResetKeep z_inflateResetKeep # define inflateSetDictionary z_inflateSetDictionary # define inflateSync z_inflateSync # define inflateSyncPoint z_inflateSyncPoint -# define inflateCopy z_inflateCopy -# define inflateReset z_inflateReset -# define inflateBack z_inflateBack -# define inflateBackEnd z_inflateBackEnd -# define compress z_compress -# define compress2 z_compress2 -# define compressBound z_compressBound -# define uncompress z_uncompress -# define adler32 z_adler32 -# define crc32 z_crc32 -# define get_crc_table z_get_crc_table +# define inflateUndermine z_inflateUndermine +# define inflateValidate z_inflateValidate +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# define uncompress2 z_uncompress2 +# endif # define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion -# define alloc_func z_alloc_func -# define free_func z_free_func -# define in_func z_in_func -# define out_func z_out_func +/* all zlib typedefs in zlib.h and zconf.h */ # define Byte z_Byte -# define uInt z_uInt -# define uLong z_uLong # define Bytef z_Bytef +# define alloc_func z_alloc_func # define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func # define intf z_intf +# define out_func z_out_func +# define uInt z_uInt # define uIntf z_uIntf +# define uLong z_uLong # define uLongf z_uLongf -# define voidpf z_voidpf # define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + #endif #if defined(__MSDOS__) && !defined(MSDOS) @@ -125,9 +231,25 @@ # endif #endif -/* Some Mac compilers merge all .h files incorrectly: */ -#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) -# define NO_DUMMY_DECL +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +#ifdef Z_SOLO + typedef unsigned long z_size_t; +#else +# define z_longlong long long +# if defined(NO_SIZE_T) + typedef unsigned NO_SIZE_T z_size_t; +# elif defined(STDC) +# include + typedef size_t z_size_t; +# else + typedef unsigned long z_size_t; +# endif +# undef z_longlong #endif /* Maximum value for memLevel in deflateInit2 */ @@ -157,7 +279,7 @@ Of course this will generally degrade compression (there's no free lunch). The memory requirements for inflate are (in bytes) 1 << windowBits - that is, 32K for windowBits=15 (default value) plus a few kilobytes + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes for small objects. */ @@ -171,6 +293,14 @@ # endif #endif +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + /* The following definitions for FAR are needed only for MSDOS mixed * model programming (small or medium model with some far allocations). * This was tested only with MSC; for other MSDOS compilers you may have @@ -284,49 +414,121 @@ typedef uLong FAR uLongf; typedef Byte *voidp; #endif -#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ -# include /* for off_t */ -# include /* for SEEK_* and off_t */ -# ifdef VMS -# include /* for off_t */ +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short # endif -# define z_off_t off_t #endif -#ifndef SEEK_SET + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) # define SEEK_SET 0 /* Seek from beginning of file. */ # define SEEK_CUR 1 /* Seek from current position. */ # define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ #endif + #ifndef z_off_t # define z_off_t long #endif -#if defined(__OS400__) -# define NO_vsnprintf -#endif - -#if defined(__MVS__) -# define NO_vsnprintf -# ifdef FAR -# undef FAR +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t # endif #endif /* MVS linker does not support external names larger than 8 bytes */ #if defined(__MVS__) -# pragma map(deflateInit_,"DEIN") -# pragma map(deflateInit2_,"DEIN2") -# pragma map(deflateEnd,"DEEND") -# pragma map(deflateBound,"DEBND") -# pragma map(inflateInit_,"ININ") -# pragma map(inflateInit2_,"ININ2") -# pragma map(inflateEnd,"INEND") -# pragma map(inflateSync,"INSY") -# pragma map(inflateSetDictionary,"INSEDI") -# pragma map(compressBound,"CMBND") -# pragma map(inflate_table,"INTABL") -# pragma map(inflate_fast,"INFA") -# pragma map(inflate_copyright,"INCOPY") + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") #endif #endif /* ZCONF_H */ diff --git a/contrib/zlib/zconf.h.included b/contrib/zlib/zconf.h.included index 9ccbb0a6f..5e1d68a00 100644 --- a/contrib/zlib/zconf.h.included +++ b/contrib/zlib/zconf.h.included @@ -1,5 +1,5 @@ /* zconf.h -- configuration of the zlib compression library - * Copyright (C) 1995-2013 Jean-loup Gailly. + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -7,8 +7,6 @@ #ifndef ZCONF_H #define ZCONF_H -/* #undef Z_PREFIX */ -/* #undef Z_HAVE_UNISTD_H */ /* * If you *really* need a unique prefix for all types and library functions, @@ -19,7 +17,7 @@ #ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ # define Z_PREFIX_SET -/* all linked symbols */ +/* all linked symbols and init macros */ # define _dist_code z__dist_code # define _length_code z__length_code # define _tr_align z__tr_align @@ -31,6 +29,7 @@ # define adler32 z_adler32 # define adler32_combine z_adler32_combine # define adler32_combine64 z_adler32_combine64 +# define adler32_z z_adler32_z # ifndef Z_SOLO # define compress z_compress # define compress2 z_compress2 @@ -39,10 +38,14 @@ # define crc32 z_crc32 # define crc32_combine z_crc32_combine # define crc32_combine64 z_crc32_combine64 +# define crc32_z z_crc32_z # define deflate z_deflate # define deflateBound z_deflateBound # define deflateCopy z_deflateCopy # define deflateEnd z_deflateEnd +# define deflateGetDictionary z_deflateGetDictionary +# define deflateInit z_deflateInit +# define deflateInit2 z_deflateInit2 # define deflateInit2_ z_deflateInit2_ # define deflateInit_ z_deflateInit_ # define deflateParams z_deflateParams @@ -69,6 +72,8 @@ # define gzeof z_gzeof # define gzerror z_gzerror # define gzflush z_gzflush +# define gzfread z_gzfread +# define gzfwrite z_gzfwrite # define gzgetc z_gzgetc # define gzgetc_ z_gzgetc_ # define gzgets z_gzgets @@ -80,7 +85,6 @@ # define gzopen_w z_gzopen_w # endif # define gzprintf z_gzprintf -# define gzvprintf z_gzvprintf # define gzputc z_gzputc # define gzputs z_gzputs # define gzread z_gzread @@ -91,32 +95,39 @@ # define gztell z_gztell # define gztell64 z_gztell64 # define gzungetc z_gzungetc +# define gzvprintf z_gzvprintf # define gzwrite z_gzwrite # endif # define inflate z_inflate # define inflateBack z_inflateBack # define inflateBackEnd z_inflateBackEnd +# define inflateBackInit z_inflateBackInit # define inflateBackInit_ z_inflateBackInit_ +# define inflateCodesUsed z_inflateCodesUsed # define inflateCopy z_inflateCopy # define inflateEnd z_inflateEnd +# define inflateGetDictionary z_inflateGetDictionary # define inflateGetHeader z_inflateGetHeader +# define inflateInit z_inflateInit +# define inflateInit2 z_inflateInit2 # define inflateInit2_ z_inflateInit2_ # define inflateInit_ z_inflateInit_ # define inflateMark z_inflateMark # define inflatePrime z_inflatePrime # define inflateReset z_inflateReset # define inflateReset2 z_inflateReset2 +# define inflateResetKeep z_inflateResetKeep # define inflateSetDictionary z_inflateSetDictionary -# define inflateGetDictionary z_inflateGetDictionary # define inflateSync z_inflateSync # define inflateSyncPoint z_inflateSyncPoint # define inflateUndermine z_inflateUndermine -# define inflateResetKeep z_inflateResetKeep +# define inflateValidate z_inflateValidate # define inflate_copyright z_inflate_copyright # define inflate_fast z_inflate_fast # define inflate_table z_inflate_table # ifndef Z_SOLO # define uncompress z_uncompress +# define uncompress2 z_uncompress2 # endif # define zError z_zError # ifndef Z_SOLO @@ -226,9 +237,19 @@ # define z_const #endif -/* Some Mac compilers merge all .h files incorrectly: */ -#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) -# define NO_DUMMY_DECL +#ifdef Z_SOLO + typedef unsigned long z_size_t; +#else +# define z_longlong long long +# if defined(NO_SIZE_T) + typedef unsigned NO_SIZE_T z_size_t; +# elif defined(STDC) +# include + typedef size_t z_size_t; +# else + typedef unsigned long z_size_t; +# endif +# undef z_longlong #endif /* Maximum value for memLevel in deflateInit2 */ @@ -258,7 +279,7 @@ Of course this will generally degrade compression (there's no free lunch). The memory requirements for inflate are (in bytes) 1 << windowBits - that is, 32K for windowBits=15 (default value) plus a few kilobytes + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes for small objects. */ diff --git a/contrib/zlib/zlib.3 b/contrib/zlib/zlib.3 new file mode 100644 index 000000000..fac206b52 --- /dev/null +++ b/contrib/zlib/zlib.3 @@ -0,0 +1,149 @@ +.TH ZLIB 3 "xx Jan 2017" +.SH NAME +zlib \- compression/decompression library +.SH SYNOPSIS +[see +.I zlib.h +for full description] +.SH DESCRIPTION +The +.I zlib +library is a general purpose data compression library. +The code is thread safe, assuming that the standard library functions +used are thread safe, such as memory allocation routines. +It provides in-memory compression and decompression functions, +including integrity checks of the uncompressed data. +This version of the library supports only one compression method (deflation) +but other algorithms may be added later +with the same stream interface. +.LP +Compression can be done in a single step if the buffers are large enough +or can be done by repeated calls of the compression function. +In the latter case, +the application must provide more input and/or consume the output +(providing more output space) before each call. +.LP +The library also supports reading and writing files in +.IR gzip (1) +(.gz) format +with an interface similar to that of stdio. +.LP +The library does not install any signal handler. +The decoder checks the consistency of the compressed data, +so the library should never crash even in the case of corrupted input. +.LP +All functions of the compression library are documented in the file +.IR zlib.h . +The distribution source includes examples of use of the library +in the files +.I test/example.c +and +.IR test/minigzip.c, +as well as other examples in the +.IR examples/ +directory. +.LP +Changes to this version are documented in the file +.I ChangeLog +that accompanies the source. +.LP +.I zlib +is built in to many languages and operating systems, including but not limited to +Java, Python, .NET, PHP, Perl, Ruby, Swift, and Go. +.LP +An experimental package to read and write files in the .zip format, +written on top of +.I zlib +by Gilles Vollant (info@winimage.com), +is available at: +.IP +http://www.winimage.com/zLibDll/minizip.html +and also in the +.I contrib/minizip +directory of the main +.I zlib +source distribution. +.SH "SEE ALSO" +The +.I zlib +web site can be found at: +.IP +http://zlib.net/ +.LP +The data format used by the +.I zlib +library is described by RFC +(Request for Comments) 1950 to 1952 in the files: +.IP +http://tools.ietf.org/html/rfc1950 (for the zlib header and trailer format) +.br +http://tools.ietf.org/html/rfc1951 (for the deflate compressed data format) +.br +http://tools.ietf.org/html/rfc1952 (for the gzip header and trailer format) +.LP +Mark Nelson wrote an article about +.I zlib +for the Jan. 1997 issue of Dr. Dobb's Journal; +a copy of the article is available at: +.IP +http://marknelson.us/1997/01/01/zlib-engine/ +.SH "REPORTING PROBLEMS" +Before reporting a problem, +please check the +.I zlib +web site to verify that you have the latest version of +.IR zlib ; +otherwise, +obtain the latest version and see if the problem still exists. +Please read the +.I zlib +FAQ at: +.IP +http://zlib.net/zlib_faq.html +.LP +before asking for help. +Send questions and/or comments to zlib@gzip.org, +or (for the Windows DLL version) to Gilles Vollant (info@winimage.com). +.SH AUTHORS AND LICENSE +Version 1.2.11.1 +.LP +Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler +.LP +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. +.LP +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: +.LP +.nr step 1 1 +.IP \n[step]. 3 +The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. +.IP \n+[step]. +Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. +.IP \n+[step]. +This notice may not be removed or altered from any source distribution. +.LP +Jean-loup Gailly Mark Adler +.br +jloup@gzip.org madler@alumni.caltech.edu +.LP +The deflate format used by +.I zlib +was defined by Phil Katz. +The deflate and +.I zlib +specifications were written by L. Peter Deutsch. +Thanks to all the people who reported problems and suggested various +improvements in +.IR zlib ; +who are too numerous to cite here. +.LP +UNIX manual page by R. P. C. Rodgers, +U.S. National Library of Medicine (rodgers@nlm.nih.gov). +.\" end of man page diff --git a/contrib/zlib/zlib.3.pdf b/contrib/zlib/zlib.3.pdf new file mode 100644 index 0000000000000000000000000000000000000000..48e6147385dc9dfc0058474f2e1efa1453409e8c GIT binary patch literal 19320 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μ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 TS4a>yCp#x9HMN+$IO_icwA6bT literal 0 HcmV?d00001 diff --git a/contrib/zlib/zlib.h b/contrib/zlib/zlib.h index 3e0c7672a..577d81e3b 100644 --- a/contrib/zlib/zlib.h +++ b/contrib/zlib/zlib.h @@ -1,7 +1,7 @@ /* zlib.h -- interface of the 'zlib' general purpose compression library - version 1.2.8, April 28th, 2013 + version 1.2.11.1, January xxth, 2017 - Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler + Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -37,12 +37,12 @@ extern "C" { #endif -#define ZLIB_VERSION "1.2.8" -#define ZLIB_VERNUM 0x1280 +#define ZLIB_VERSION "1.2.11.1-motley" +#define ZLIB_VERNUM 0x12b1 #define ZLIB_VER_MAJOR 1 #define ZLIB_VER_MINOR 2 -#define ZLIB_VER_REVISION 8 -#define ZLIB_VER_SUBREVISION 0 +#define ZLIB_VER_REVISION 11 +#define ZLIB_VER_SUBREVISION 1 /* The 'zlib' compression library provides in-memory compression and @@ -65,7 +65,8 @@ extern "C" { with "gz". The gzip format is different from the zlib format. gzip is a gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. - This library can optionally read and write gzip streams in memory as well. + This library can optionally read and write gzip and raw deflate streams in + memory as well. The zlib format was designed to be compact and fast for use in memory and on communications channels. The gzip format was designed for single- @@ -74,7 +75,7 @@ extern "C" { The library does not install any signal handler. The decoder checks the consistency of the compressed data, so the library should never crash - even in case of corrupted input. + even in the case of corrupted input. */ typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); @@ -87,7 +88,7 @@ typedef struct z_stream_s { uInt avail_in; /* number of bytes available at next_in */ uLong total_in; /* total number of input bytes read so far */ - Bytef *next_out; /* next output byte should be put there */ + Bytef *next_out; /* next output byte will go here */ uInt avail_out; /* remaining free space at next_out */ uLong total_out; /* total number of bytes output so far */ @@ -98,8 +99,9 @@ typedef struct z_stream_s { free_func zfree; /* used to free the internal state */ voidpf opaque; /* private data object passed to zalloc and zfree */ - int data_type; /* best guess about the data type: binary or text */ - uLong adler; /* adler32 value of the uncompressed data */ + int data_type; /* best guess about the data type: binary or text + for deflate, or the decoding state for inflate */ + uLong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ uLong reserved; /* reserved for future use */ } z_stream; @@ -142,7 +144,9 @@ typedef gz_header FAR *gz_headerp; zalloc must return Z_NULL if there is not enough memory for the object. If zlib is used in a multi-threaded application, zalloc and zfree must be - thread safe. + thread safe. In that case, zlib is thread-safe. When zalloc and zfree are + Z_NULL on entry to the initialization function, they are set to internal + routines that use the standard library functions malloc() and free(). On 16-bit systems, the functions zalloc and zfree must be able to allocate exactly 65536 bytes, but will not be required to allocate more than this if @@ -155,7 +159,7 @@ typedef gz_header FAR *gz_headerp; The fields total_in and total_out can be used for statistics or progress reports. After compression, total_in holds the total size of the - uncompressed data and may be saved for use in the decompressor (particularly + uncompressed data and may be saved for use by the decompressor (particularly if the decompressor wants to decompress everything in a single step). */ @@ -200,7 +204,7 @@ typedef gz_header FAR *gz_headerp; #define Z_TEXT 1 #define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ #define Z_UNKNOWN 2 -/* Possible values of the data_type field (though see inflate()) */ +/* Possible values of the data_type field for deflate() */ #define Z_DEFLATED 8 /* The deflate compression method (the only one supported in this version) */ @@ -258,11 +262,11 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); enough room in the output buffer), next_in and avail_in are updated and processing will resume at this point for the next call of deflate(). - - Provide more output starting at next_out and update next_out and avail_out + - Generate more output starting at next_out and update next_out and avail_out accordingly. This action is forced if the parameter flush is non zero. Forcing flush frequently degrades the compression ratio, so this parameter - should be set only when necessary (in interactive applications). Some - output may be provided even if flush is not set. + should be set only when necessary. Some output may be provided even if + flush is zero. Before the call of deflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more @@ -271,7 +275,9 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK and with zero avail_out, it must be called again after making room in the output - buffer because there might be more output pending. + buffer because there might be more output pending. See deflatePending(), + which can be used if desired to determine whether or not there is more ouput + in that case. Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to decide how much data to accumulate before producing output, in order to @@ -292,8 +298,8 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. This completes the current deflate block and follows it with an empty fixed codes block that is 10 bits long. This assures that enough bytes are output - in order for the decompressor to finish the block before the empty fixed code - block. + in order for the decompressor to finish the block before the empty fixed + codes block. If flush is set to Z_BLOCK, a deflate block is completed and emitted, as for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to @@ -319,34 +325,38 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); If the parameter flush is set to Z_FINISH, pending input is processed, pending output is flushed and deflate returns with Z_STREAM_END if there was - enough output space; if deflate returns with Z_OK, this function must be - called again with Z_FINISH and more output space (updated avail_out) but no - more input data, until it returns with Z_STREAM_END or an error. After - deflate has returned Z_STREAM_END, the only possible operations on the stream - are deflateReset or deflateEnd. + enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this + function must be called again with Z_FINISH and more output space (updated + avail_out) but no more input data, until it returns with Z_STREAM_END or an + error. After deflate has returned Z_STREAM_END, the only possible operations + on the stream are deflateReset or deflateEnd. - Z_FINISH can be used immediately after deflateInit if all the compression - is to be done in a single step. In this case, avail_out must be at least the - value returned by deflateBound (see below). Then deflate is guaranteed to - return Z_STREAM_END. If not enough output space is provided, deflate will - not return Z_STREAM_END, and it must be called again as described above. + Z_FINISH can be used in the first deflate call after deflateInit if all the + compression is to be done in a single step. In order to complete in one + call, avail_out must be at least the value returned by deflateBound (see + below). Then deflate is guaranteed to return Z_STREAM_END. If not enough + output space is provided, deflate will not return Z_STREAM_END, and it must + be called again as described above. - deflate() sets strm->adler to the adler32 checksum of all input read - so far (that is, total_in bytes). + deflate() sets strm->adler to the Adler-32 checksum of all input read + so far (that is, total_in bytes). If a gzip stream is being generated, then + strm->adler will be the CRC-32 checksum of the input read so far. (See + deflateInit2 below.) deflate() may update strm->data_type if it can make a good guess about - the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered - binary. This field is only for information purposes and does not affect the - compression algorithm in any manner. + the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is + considered binary. This field is only for information purposes and does not + affect the compression algorithm in any manner. deflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if all input has been consumed and all output has been produced (only when flush is set to Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example - if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible - (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not - fatal, and deflate() can be called again with more input and more output - space to continue compressing. + if next_in or next_out was Z_NULL or the state was inadvertently written over + by the application), or Z_BUF_ERROR if no progress is possible (for example + avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and + deflate() can be called again with more input and more output space to + continue compressing. */ @@ -369,23 +379,21 @@ ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); Initializes the internal stream state for decompression. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by - the caller. If next_in is not Z_NULL and avail_in is large enough (the - exact value depends on the compression method), inflateInit determines the - compression method from the zlib header and allocates all data structures - accordingly; otherwise the allocation will be deferred to the first call of - inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to - use default allocation functions. + the caller. In the current version of inflate, the provided input is not + read or consumed. The allocation of a sliding window will be deferred to + the first call of inflate (if the decompression does not complete on the + first call). If zalloc and zfree are set to Z_NULL, inflateInit updates + them to use default allocation functions. inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the version assumed by the caller, or Z_STREAM_ERROR if the parameters are invalid, such as a null pointer to the structure. msg is set to null if - there is no error message. inflateInit does not perform any decompression - apart from possibly reading the zlib header if present: actual decompression - will be done by inflate(). (So next_in and avail_in may be modified, but - next_out and avail_out are unused and unchanged.) The current implementation - of inflateInit() does not process any header information -- that is deferred - until inflate() is called. + there is no error message. inflateInit does not perform any decompression. + Actual decompression will be done by inflate(). So next_in, and avail_in, + next_out, and avail_out are unused and unchanged. The current + implementation of inflateInit() does not process any header information -- + that is deferred until inflate() is called. */ @@ -401,17 +409,20 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); - Decompress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not - enough room in the output buffer), next_in is updated and processing will - resume at this point for the next call of inflate(). + enough room in the output buffer), then next_in and avail_in are updated + accordingly, and processing will resume at this point for the next call of + inflate(). - - Provide more output starting at next_out and update next_out and avail_out + - Generate more output starting at next_out and update next_out and avail_out accordingly. inflate() provides as much output as possible, until there is no more input data or no more space in the output buffer (see below about the flush parameter). Before the call of inflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more - output, and updating the next_* and avail_* values accordingly. The + output, and updating the next_* and avail_* values accordingly. If the + caller of inflate() does not provide both available input and available + output space, it is possible that there will be no progress made. The application can consume the uncompressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of inflate(). If inflate returns Z_OK and with zero avail_out, it must be @@ -428,7 +439,7 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); gets to the end of that block, or when it runs out of data. The Z_BLOCK option assists in appending to or combining deflate streams. - Also to assist in this, on return inflate() will set strm->data_type to the + To assist in this, on return inflate() always sets strm->data_type to the number of unused bits in the last byte taken from strm->next_in, plus 64 if inflate() is currently decoding the last block in the deflate stream, plus 128 if inflate() returned immediately after decoding an end-of-block code or @@ -454,7 +465,7 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); this case all pending input is processed and all pending output is flushed; avail_out must be large enough to hold all of the uncompressed data for the operation to complete. (The size of the uncompressed data may have been - saved by the compressor for this purpose.) The use of Z_FINISH is not + saved by the compressor for this purpose.) The use of Z_FINISH is not required to perform an inflation in one step. However it may be used to inform inflate that a faster approach can be used for the single inflate() call. Z_FINISH also informs inflate to not maintain a sliding window if the @@ -476,32 +487,33 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); chosen by the compressor and returns Z_NEED_DICT; otherwise it sets strm->adler to the Adler-32 checksum of all output produced so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described - below. At the end of the stream, inflate() checks that its computed adler32 + below. At the end of the stream, inflate() checks that its computed Adler-32 checksum is equal to that saved by the compressor and returns Z_STREAM_END only if the checksum is correct. inflate() can decompress and check either zlib-wrapped or gzip-wrapped deflate data. The header type is detected automatically, if requested when initializing with inflateInit2(). Any information contained in the gzip - header is not retained, so applications that need that information should - instead use raw inflate, see inflateInit2() below, or inflateBack() and - perform their own processing of the gzip header and trailer. When processing + header is not retained unless inflateGetHeader() is used. When processing gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output - producted so far. The CRC-32 is checked against the gzip trailer. + produced so far. The CRC-32 is checked against the gzip trailer, as is the + uncompressed length, modulo 2^32. inflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if the end of the compressed data has been reached and all uncompressed output has been produced, Z_NEED_DICT if a preset dictionary is needed at this point, Z_DATA_ERROR if the input data was corrupted (input stream not conforming to the zlib format or incorrect check - value), Z_STREAM_ERROR if the stream structure was inconsistent (for example - next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory, - Z_BUF_ERROR if no progress is possible or if there was not enough room in the - output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + value, in which case strm->msg points to a string with a more specific + error), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL, or the state was inadvertently written over + by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR + if no progress was possible or if there was not enough room in the output + buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and inflate() can be called again with more input and more output space to continue decompressing. If Z_DATA_ERROR is returned, the application may then call inflateSync() to look for a good compression block if a partial - recovery of the data is desired. + recovery of the data is to be attempted. */ @@ -511,9 +523,8 @@ ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); This function discards any unprocessed input and does not flush any pending output. - inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state - was inconsistent. In the error case, msg may be set but then points to a - static string (which must not be deallocated). + inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state + was inconsistent. */ @@ -544,16 +555,29 @@ ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, compression at the expense of memory usage. The default value is 15 if deflateInit is used instead. + For the current implementation of deflate(), a windowBits value of 8 (a + window size of 256 bytes) is not supported. As a result, a request for 8 + will result in 9 (a 512-byte window). In that case, providing 8 to + inflateInit2() will result in an error when the zlib header with 9 is + checked against the initialization of inflate(). The remedy is to not use 8 + with deflateInit2() with this initialization, or at least in that case use 9 + with inflateInit2(). + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits determines the window size. deflate() will then generate raw deflate data - with no zlib header or trailer, and will not compute an adler32 check value. + with no zlib header or trailer, and will not compute a check value. windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper. The gzip header will have no file name, no extra data, no comment, no modification time (set to zero), no - header crc, and the operating system will be set to 255 (unknown). If a - gzip stream is being written, strm->adler is a crc32 instead of an adler32. + header crc, and the operating system will be set to the appropriate value, + if the operating system was determined at compile time. If a gzip stream is + being written, strm->adler is a CRC-32 instead of an Adler-32. + + For raw deflate or gzip encoding, a request for a 256-byte window is + rejected as invalid, since only the zlib header provides a means of + transmitting the window size to the decompressor. The memLevel parameter specifies how much memory should be allocated for the internal compression state. memLevel=1 uses minimum memory but is @@ -614,12 +638,12 @@ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, addition, the current implementation of deflate will use at most the window size minus 262 bytes of the provided dictionary. - Upon return of this function, strm->adler is set to the adler32 value + Upon return of this function, strm->adler is set to the Adler-32 value of the dictionary; the decompressor may later use this value to determine - which dictionary has been used by the compressor. (The adler32 value + which dictionary has been used by the compressor. (The Adler-32 value applies to the whole dictionary even if only a subset of the dictionary is actually used by the compressor.) If a raw deflate was requested, then the - adler32 value is not computed and strm->adler is not set. + Adler-32 value is not computed and strm->adler is not set. deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is @@ -628,6 +652,28 @@ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, not perform any compression: this will be done by deflate(). */ +ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by deflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If deflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + deflateGetDictionary() may return a length less than the window size, even + when more than the window size in input has been provided. It may return up + to 258 bytes less in that case, due to how zlib's implementation of deflate + manages the sliding window and lookahead for matches, where matches can be + up to 258 bytes long. If the application needs the last window-size bytes of + input, then that would need to be saved by the application outside of zlib. + + deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, z_streamp source)); /* @@ -648,10 +694,10 @@ ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); /* - This function is equivalent to deflateEnd followed by deflateInit, - but does not free and reallocate all the internal compression state. The - stream will keep the same compression level and any other attributes that - may have been set by deflateInit2. + This function is equivalent to deflateEnd followed by deflateInit, but + does not free and reallocate the internal compression state. The stream + will leave the compression level and any other attributes that may have been + set unchanged. deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). @@ -662,20 +708,37 @@ ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, int strategy)); /* Dynamically update the compression level and compression strategy. The - interpretation of level and strategy is as in deflateInit2. This can be + interpretation of level and strategy is as in deflateInit2(). This can be used to switch between compression and straight copy of the input data, or to switch to a different kind of input data requiring a different strategy. - If the compression level is changed, the input available so far is - compressed with the old level (and may be flushed); the new level will take - effect only at the next call of deflate(). + If the compression approach (which is a function of the level) or the + strategy is changed, and if there have been any deflate() calls since the + state was initialized or reset, then the input available so far is + compressed with the old level and strategy using deflate(strm, Z_BLOCK). + There are three approaches for the compression levels 0, 1..3, and 4..9 + respectively. The new level and strategy will take effect at the next call + of deflate(). - Before the call of deflateParams, the stream state must be set as for - a call of deflate(), since the currently available input may have to be - compressed and flushed. In particular, strm->avail_out must be non-zero. + If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does + not have enough output space to complete, then the parameter change will not + take effect. In this case, deflateParams() can be called again with the + same parameters and more output space to try again. - deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source - stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if - strm->avail_out was zero. + In order to assure a change in the parameters on the first try, the + deflate stream should be flushed using deflate() with Z_BLOCK or other flush + request until strm.avail_out is not zero, before calling deflateParams(). + Then no more input data should be provided before the deflateParams() call. + If this is done, the old level and strategy will be applied to the data + compressed before deflateParams(), and the new level and strategy will be + applied to the the data compressed after deflateParams(). + + deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream + state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if + there was not enough output space to complete the compression of the + available input data before a change in the strategy or approach. Note that + in the case of a Z_BUF_ERROR, the parameters are not changed. A return + value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be + retried with more output space. */ ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, @@ -793,7 +856,7 @@ ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, is for use with other formats that use the deflate compressed data format such as zip. Those formats provide their own check values. If a custom format is developed using the raw deflate format for compressed data, it is - recommended that a check value such as an adler32 or a crc32 be applied to + recommended that a check value such as an Adler-32 or a CRC-32 be applied to the uncompressed data as is done in the zlib, gzip, and zip formats. For most applications, the zlib format should be used as is. Note that comments above on the use in deflateInit2() applies to the magnitude of windowBits. @@ -802,7 +865,10 @@ ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, 32 to windowBits to enable zlib and gzip decoding with automatic header detection, or add 16 to decode only the gzip format (the zlib format will return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a - crc32 instead of an adler32. + CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see + below), inflate() will not automatically decode concatenated gzip streams. + inflate() will return Z_STREAM_END at the end of the gzip stream. The state + would need to be reset to continue decoding a subsequent gzip stream. inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the @@ -823,7 +889,7 @@ ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, Initializes the decompression dictionary from the given uncompressed byte sequence. This function must be called immediately after a call of inflate, if that call returned Z_NEED_DICT. The dictionary chosen by the compressor - can be determined from the adler32 value returned by that call of inflate. + can be determined from the Adler-32 value returned by that call of inflate. The compressor and decompressor must use exactly the same dictionary (see deflateSetDictionary). For raw inflate, this function can be called at any time to set the dictionary. If the provided dictionary is smaller than the @@ -834,7 +900,7 @@ ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the - expected one (incorrect adler32 value). inflateSetDictionary does not + expected one (incorrect Adler-32 value). inflateSetDictionary does not perform any decompression: this will be done by subsequent calls of inflate(). */ @@ -892,7 +958,7 @@ ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); /* This function is equivalent to inflateEnd followed by inflateInit, - but does not free and reallocate all the internal decompression state. The + but does not free and reallocate the internal decompression state. The stream will keep attributes that may have been set by inflateInit2. inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source @@ -904,7 +970,9 @@ ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, /* This function is the same as inflateReset, but it also permits changing the wrap and window size requests. The windowBits parameter is interpreted - the same as it is for inflateInit2. + the same as it is for inflateInit2. If the window size is changed, then the + memory allocated for the window is freed, and the window will be reallocated + by inflate() if needed. inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL), or if @@ -956,7 +1024,7 @@ ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); location in the input stream can be determined from avail_in and data_type as noted in the description for the Z_BLOCK flush parameter for inflate. - inflateMark returns the value noted above or -1 << 16 if the provided + inflateMark returns the value noted above, or -65536 if the provided source stream state was inconsistent. */ @@ -1048,9 +1116,9 @@ ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, This routine would normally be used in a utility that reads zip or gzip files and writes out uncompressed files. The utility would decode the header and process the trailer on its own, hence this routine expects only - the raw deflate stream to decompress. This is different from the normal - behavior of inflate(), which expects either a zlib or gzip header and - trailer around the deflate stream. + the raw deflate stream to decompress. This is different from the default + behavior of inflate(), which expects a zlib header and trailer around the + deflate stream. inflateBack() uses two subroutines supplied by the caller that are then called by inflateBack() for input and output. inflateBack() calls those @@ -1059,12 +1127,12 @@ ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, parameters and return types are defined above in the in_func and out_func typedefs. inflateBack() will call in(in_desc, &buf) which should return the number of bytes of provided input, and a pointer to that input in buf. If - there is no input available, in() must return zero--buf is ignored in that - case--and inflateBack() will return a buffer error. inflateBack() will call - out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() - should return zero on success, or non-zero on failure. If out() returns - non-zero, inflateBack() will return with an error. Neither in() nor out() - are permitted to change the contents of the window provided to + there is no input available, in() must return zero -- buf is ignored in that + case -- and inflateBack() will return a buffer error. inflateBack() will + call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. + out() should return zero on success, or non-zero on failure. If out() + returns non-zero, inflateBack() will return with an error. Neither in() nor + out() are permitted to change the contents of the window provided to inflateBackInit(), which is also the buffer that out() uses to write from. The length written by out() will be at most the window size. Any non-zero amount of input may be provided by in(). @@ -1092,7 +1160,7 @@ ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, using strm->next_in which will be Z_NULL only if in() returned an error. If strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning non-zero. (in() will always be called before out(), so strm->next_in is - assured to be defined if out() returns non-zero.) Note that inflateBack() + assured to be defined if out() returns non-zero.) Note that inflateBack() cannot return Z_OK. */ @@ -1114,7 +1182,7 @@ ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); 7.6: size of z_off_t Compiler, assembler, and debug options: - 8: DEBUG + 8: ZLIB_DEBUG 9: ASMV or ASMINF -- use ASM code 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention 11: 0 (reserved) @@ -1164,7 +1232,8 @@ ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least the value returned by compressBound(sourceLen). Upon exit, destLen is the actual size of the - compressed buffer. + compressed data. compress() is equivalent to compress2() with a level + parameter of Z_DEFAULT_COMPRESSION. compress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output @@ -1180,7 +1249,7 @@ ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least the value returned by compressBound(sourceLen). Upon exit, destLen is the actual size of the - compressed buffer. + compressed data. compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, @@ -1203,7 +1272,7 @@ ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, uncompressed data. (The size of the uncompressed data must have been saved previously by the compressor and transmitted to the decompressor by some mechanism outside the scope of this compression library.) Upon exit, destLen - is the actual size of the uncompressed buffer. + is the actual size of the uncompressed data. uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output @@ -1212,6 +1281,14 @@ ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, buffer with the uncompressed data up to that point. */ +ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen)); +/* + Same as uncompress, except that sourceLen is a pointer, where the + length of the source is *sourceLen. On return, *sourceLen is the number of + source bytes consumed. +*/ + /* gzip file access functions */ /* @@ -1290,10 +1367,9 @@ ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); default buffer size is 8192 bytes. This function must be called after gzopen() or gzdopen(), and before any other calls that read or write the file. The buffer memory allocation is always deferred to the first read or - write. Two buffers are allocated, either both of the specified size when - writing, or one of the specified size and the other twice that size when - reading. A larger buffer size of, for example, 64K or 128K bytes will - noticeably increase the speed of decompression (reading). + write. Three times that size in buffer space is allocated. A larger buffer + size of, for example, 64K or 128K bytes will noticeably increase the speed + of decompression (reading). The new buffer size also affects the maximum length for gzprintf(). @@ -1304,10 +1380,12 @@ ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); /* Dynamically update the compression level or strategy. See the description - of deflateInit2 for the meaning of these parameters. + of deflateInit2 for the meaning of these parameters. Previously provided + data is flushed before the parameter change. - gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not - opened for writing. + gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not + opened for writing, Z_ERRNO if there is an error writing the flushed data, + or Z_MEM_ERROR if there is a memory allocation error. */ ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); @@ -1335,7 +1413,35 @@ ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); case. gzread returns the number of uncompressed bytes actually read, less than - len for end of file, or -1 for error. + len for end of file, or -1 for error. If len is too large to fit in an int, + then nothing is read, -1 is returned, and the error state is set to + Z_STREAM_ERROR. +*/ + +ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, + gzFile file)); +/* + Read up to nitems items of size size from file to buf, otherwise operating + as gzread() does. This duplicates the interface of stdio's fread(), with + size_t request and return types. If the library defines size_t, then + z_size_t is identical to size_t. If not, then z_size_t is an unsigned + integer type that can contain a pointer. + + gzfread() returns the number of full items read of size size, or zero if + the end of the file was reached and a full item could not be read, or if + there was an error. gzerror() must be consulted if zero is returned in + order to determine if there was an error. If the multiplication of size and + nitems overflows, i.e. the product does not fit in a z_size_t, then nothing + is read, zero is returned, and the error state is set to Z_STREAM_ERROR. + + In the event that the end of file is reached and only a partial item is + available at the end, i.e. the remaining uncompressed data length is not a + multiple of size, then the final partial item is nevetheless read into buf + and the end-of-file flag is set. The length of the partial item read is not + provided, but could be inferred from the result of gztell(). This behavior + is the same as the behavior of fread() implementations in common libraries, + but it prevents the direct use of gzfread() to read a concurrently written + file, reseting and retrying on end-of-file, when size is not 1. */ ZEXTERN int ZEXPORT gzwrite OF((gzFile file, @@ -1346,19 +1452,33 @@ ZEXTERN int ZEXPORT gzwrite OF((gzFile file, error. */ +ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, + z_size_t nitems, gzFile file)); +/* + gzfwrite() writes nitems items of size size from buf to file, duplicating + the interface of stdio's fwrite(), with size_t request and return types. If + the library defines size_t, then z_size_t is identical to size_t. If not, + then z_size_t is an unsigned integer type that can contain a pointer. + + gzfwrite() returns the number of full items written of size size, or zero + if there was an error. If the multiplication of size and nitems overflows, + i.e. the product does not fit in a z_size_t, then nothing is written, zero + is returned, and the error state is set to Z_STREAM_ERROR. +*/ + ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); /* Converts, formats, and writes the arguments to the compressed file under control of the format string, as in fprintf. gzprintf returns the number of - uncompressed bytes actually written, or 0 in case of error. The number of - uncompressed bytes written is limited to 8191, or one less than the buffer - size given to gzbuffer(). The caller should assure that this limit is not - exceeded. If it is exceeded, then gzprintf() will return an error (0) with - nothing written. In this case, there may also be a buffer overflow with - unpredictable consequences, which is possible only if zlib was compiled with - the insecure functions sprintf() or vsprintf() because the secure snprintf() - or vsnprintf() functions were not available. This can be determined using - zlibCompileFlags(). + uncompressed bytes actually written, or a negative zlib error code in case + of error. The number of uncompressed bytes written is limited to 8191, or + one less than the buffer size given to gzbuffer(). The caller should assure + that this limit is not exceeded. If it is exceeded, then gzprintf() will + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. + This can be determined using zlibCompileFlags(). */ ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); @@ -1418,7 +1538,7 @@ ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); If the flush parameter is Z_FINISH, the remaining data is written and the gzip stream is completed in the output. If gzwrite() is called again, a new gzip stream will be started in the output. gzread() is able to read such - concatented gzip streams. + concatenated gzip streams. gzflush should be called only when strictly necessary because it will degrade compression if called too often. @@ -1572,7 +1692,7 @@ ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); return the updated checksum. If buf is Z_NULL, this function returns the required initial value for the checksum. - An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed much faster. Usage example: @@ -1585,6 +1705,12 @@ ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); if (adler != original_adler) error(); */ +ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as adler32(), but with a size_t length. +*/ + /* ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, z_off_t len2)); @@ -1614,6 +1740,12 @@ ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); if (crc != original_crc) error(); */ +ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as crc32(), but with a size_t length. +*/ + /* ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); @@ -1644,19 +1776,35 @@ ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, unsigned char FAR *window, const char *version, int stream_size)); -#define deflateInit(strm, level) \ - deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) -#define inflateInit(strm) \ - inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) -#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ - deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ - (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) -#define inflateInit2(strm, windowBits) \ - inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ - (int)sizeof(z_stream)) -#define inflateBackInit(strm, windowBits, window) \ - inflateBackInit_((strm), (windowBits), (window), \ - ZLIB_VERSION, (int)sizeof(z_stream)) +#ifdef Z_PREFIX_SET +# define z_deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define z_inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#else +# define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#endif #ifndef Z_SOLO @@ -1676,10 +1824,10 @@ ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ #ifdef Z_PREFIX_SET # undef z_gzgetc # define z_gzgetc(g) \ - ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) #else # define gzgetc(g) \ - ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) #endif /* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or @@ -1737,16 +1885,13 @@ ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ #endif /* !Z_SOLO */ -/* hack for buggy compilers */ -#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) - struct internal_state {int dummy;}; -#endif - /* undocumented functions */ ZEXTERN const char * ZEXPORT zError OF((int)); ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); +ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp)); ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); #if defined(_WIN32) && !defined(Z_SOLO) diff --git a/contrib/zlib/zlib.map b/contrib/zlib/zlib.map new file mode 100644 index 000000000..82ce98cf7 --- /dev/null +++ b/contrib/zlib/zlib.map @@ -0,0 +1,94 @@ +ZLIB_1.2.0 { + global: + compressBound; + deflateBound; + inflateBack; + inflateBackEnd; + inflateBackInit_; + inflateCopy; + local: + deflate_copyright; + inflate_copyright; + inflate_fast; + inflate_table; + zcalloc; + zcfree; + z_errmsg; + gz_error; + gz_intmax; + _*; +}; + +ZLIB_1.2.0.2 { + gzclearerr; + gzungetc; + zlibCompileFlags; +} ZLIB_1.2.0; + +ZLIB_1.2.0.8 { + deflatePrime; +} ZLIB_1.2.0.2; + +ZLIB_1.2.2 { + adler32_combine; + crc32_combine; + deflateSetHeader; + inflateGetHeader; +} ZLIB_1.2.0.8; + +ZLIB_1.2.2.3 { + deflateTune; + gzdirect; +} ZLIB_1.2.2; + +ZLIB_1.2.2.4 { + inflatePrime; +} ZLIB_1.2.2.3; + +ZLIB_1.2.3.3 { + adler32_combine64; + crc32_combine64; + gzopen64; + gzseek64; + gztell64; + inflateUndermine; +} ZLIB_1.2.2.4; + +ZLIB_1.2.3.4 { + inflateReset2; + inflateMark; +} ZLIB_1.2.3.3; + +ZLIB_1.2.3.5 { + gzbuffer; + gzoffset; + gzoffset64; + gzclose_r; + gzclose_w; +} ZLIB_1.2.3.4; + +ZLIB_1.2.5.1 { + deflatePending; +} ZLIB_1.2.3.5; + +ZLIB_1.2.5.2 { + deflateResetKeep; + gzgetc_; + inflateResetKeep; +} ZLIB_1.2.5.1; + +ZLIB_1.2.7.1 { + inflateGetDictionary; + gzvprintf; +} ZLIB_1.2.5.2; + +ZLIB_1.2.9 { + inflateCodesUsed; + inflateValidate; + uncompress2; + gzfread; + gzfwrite; + deflateGetDictionary; + adler32_z; + crc32_z; +} ZLIB_1.2.7.1; diff --git a/contrib/zlib/zlib.pc.in b/contrib/zlib/zlib.pc.in new file mode 100644 index 000000000..7e5acf9c7 --- /dev/null +++ b/contrib/zlib/zlib.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +sharedlibdir=@sharedlibdir@ +includedir=@includedir@ + +Name: zlib +Description: zlib compression library +Version: @VERSION@ + +Requires: +Libs: -L${libdir} -L${sharedlibdir} -lz +Cflags: -I${includedir} diff --git a/contrib/zlib/zlib2ansi b/contrib/zlib/zlib2ansi new file mode 100644 index 000000000..15e3e165f --- /dev/null +++ b/contrib/zlib/zlib2ansi @@ -0,0 +1,152 @@ +#!/usr/bin/perl + +# Transform K&R C function definitions into ANSI equivalent. +# +# Author: Paul Marquess +# Version: 1.0 +# Date: 3 October 2006 + +# TODO +# +# Asumes no function pointer parameters. unless they are typedefed. +# Assumes no literal strings that look like function definitions +# Assumes functions start at the beginning of a line + +use strict; +use warnings; + +local $/; +$_ = <>; + +my $sp = qr{ \s* (?: /\* .*? \*/ )? \s* }x; # assume no nested comments + +my $d1 = qr{ $sp (?: [\w\*\s]+ $sp)* $sp \w+ $sp [\[\]\s]* $sp }x ; +my $decl = qr{ $sp (?: \w+ $sp )+ $d1 }xo ; +my $dList = qr{ $sp $decl (?: $sp , $d1 )* $sp ; $sp }xo ; + + +while (s/^ + ( # Start $1 + ( # Start $2 + .*? # Minimal eat content + ( ^ \w [\w\s\*]+ ) # $3 -- function name + \s* # optional whitespace + ) # $2 - Matched up to before parameter list + + \( \s* # Literal "(" + optional whitespace + ( [^\)]+ ) # $4 - one or more anythings except ")" + \s* \) # optional whitespace surrounding a Literal ")" + + ( (?: $dList )+ ) # $5 + + $sp ^ { # literal "{" at start of line + ) # Remember to $1 + //xsom + ) +{ + my $all = $1 ; + my $prefix = $2; + my $param_list = $4 ; + my $params = $5; + + StripComments($params); + StripComments($param_list); + $param_list =~ s/^\s+//; + $param_list =~ s/\s+$//; + + my $i = 0 ; + my %pList = map { $_ => $i++ } + split /\s*,\s*/, $param_list; + my $pMatch = '(\b' . join('|', keys %pList) . '\b)\W*$' ; + + my @params = split /\s*;\s*/, $params; + my @outParams = (); + foreach my $p (@params) + { + if ($p =~ /,/) + { + my @bits = split /\s*,\s*/, $p; + my $first = shift @bits; + $first =~ s/^\s*//; + push @outParams, $first; + $first =~ /^(\w+\s*)/; + my $type = $1 ; + push @outParams, map { $type . $_ } @bits; + } + else + { + $p =~ s/^\s+//; + push @outParams, $p; + } + } + + + my %tmp = map { /$pMatch/; $_ => $pList{$1} } + @outParams ; + + @outParams = map { " $_" } + sort { $tmp{$a} <=> $tmp{$b} } + @outParams ; + + print $prefix ; + print "(\n" . join(",\n", @outParams) . ")\n"; + print "{" ; + +} + +# Output any trailing code. +print ; +exit 0; + + +sub StripComments +{ + + no warnings; + + # Strip C & C++ coments + # From the perlfaq + $_[0] =~ + + s{ + /\* ## Start of /* ... */ comment + [^*]*\*+ ## Non-* followed by 1-or-more *'s + ( + [^/*][^*]*\*+ + )* ## 0-or-more things which don't start with / + ## but do end with '*' + / ## End of /* ... */ comment + + | ## OR C++ Comment + // ## Start of C++ comment // + [^\n]* ## followed by 0-or-more non end of line characters + + | ## OR various things which aren't comments: + + ( + " ## Start of " ... " string + ( + \\. ## Escaped char + | ## OR + [^"\\] ## Non "\ + )* + " ## End of " ... " string + + | ## OR + + ' ## Start of ' ... ' string + ( + \\. ## Escaped char + | ## OR + [^'\\] ## Non '\ + )* + ' ## End of ' ... ' string + + | ## OR + + . ## Anything other char + [^/"'\\]* ## Chars which doesn't start a comment, string or escape + ) + }{$2}gxs; + +} diff --git a/contrib/zlib/zutil.c b/contrib/zlib/zutil.c index 23d2ebef0..dcab28a0d 100644 --- a/contrib/zlib/zutil.c +++ b/contrib/zlib/zutil.c @@ -1,5 +1,5 @@ /* zutil.c -- target dependent utility functions for the compression library - * Copyright (C) 1995-2005, 2010, 2011, 2012 Jean-loup Gailly. + * Copyright (C) 1995-2017 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -10,21 +10,18 @@ # include "gzguts.h" #endif -#ifndef NO_DUMMY_DECL -struct internal_state {int dummy;}; /* for buggy compilers */ -#endif - z_const char * const z_errmsg[10] = { -"need dictionary", /* Z_NEED_DICT 2 */ -"stream end", /* Z_STREAM_END 1 */ -"", /* Z_OK 0 */ -"file error", /* Z_ERRNO (-1) */ -"stream error", /* Z_STREAM_ERROR (-2) */ -"data error", /* Z_DATA_ERROR (-3) */ -"insufficient memory", /* Z_MEM_ERROR (-4) */ -"buffer error", /* Z_BUF_ERROR (-5) */ -"incompatible version",/* Z_VERSION_ERROR (-6) */ -""}; + (z_const char *)"need dictionary", /* Z_NEED_DICT 2 */ + (z_const char *)"stream end", /* Z_STREAM_END 1 */ + (z_const char *)"", /* Z_OK 0 */ + (z_const char *)"file error", /* Z_ERRNO (-1) */ + (z_const char *)"stream error", /* Z_STREAM_ERROR (-2) */ + (z_const char *)"data error", /* Z_DATA_ERROR (-3) */ + (z_const char *)"insufficient memory", /* Z_MEM_ERROR (-4) */ + (z_const char *)"buffer error", /* Z_BUF_ERROR (-5) */ + (z_const char *)"incompatible version",/* Z_VERSION_ERROR (-6) */ + (z_const char *)"" +}; const char * ZEXPORT zlibVersion() @@ -61,7 +58,7 @@ uLong ZEXPORT zlibCompileFlags() case 8: flags += 2 << 6; break; default: flags += 3 << 6; } -#ifdef DEBUG +#ifdef ZLIB_DEBUG flags += 1 << 8; #endif #if defined(ASMV) || defined(ASMINF) @@ -115,8 +112,8 @@ uLong ZEXPORT zlibCompileFlags() return flags; } -#ifdef DEBUG - +#ifdef ZLIB_DEBUG +#include # ifndef verbose # define verbose 0 # endif @@ -139,8 +136,8 @@ const char * ZEXPORT zError(err) return ERR_MSG(err); } -#if defined(_WIN32_WCE) - /* The Microsoft C Run-Time Library for Windows CE doesn't have +#if defined(_WIN32_WCE) && _WIN32_WCE < 0x800 + /* The older Microsoft C Run-Time Library for Windows CE doesn't have * errno. We define it as a global variable to simplify porting. * Its value is always 0 and should not be used. */ @@ -219,9 +216,11 @@ local ptr_table table[MAX_PTR]; voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) { - voidpf buf = opaque; /* just to make some compilers happy */ + voidpf buf; ulg bsize = (ulg)items*size; + (void)opaque; + /* If we allocate less than 65520 bytes, we assume that farmalloc * will return a usable pointer which doesn't have to be normalized. */ @@ -244,6 +243,9 @@ voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) { int n; + + (void)opaque; + if (*(ush*)&ptr != 0) { /* object < 64K */ farfree(ptr); return; @@ -259,7 +261,6 @@ void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) next_ptr--; return; } - ptr = opaque; /* just to make some compilers happy */ Assert(0, "zcfree: ptr not found"); } @@ -278,13 +279,13 @@ void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size) { - if (opaque) opaque = 0; /* to make compiler happy */ + (void)opaque; return _halloc((long)items, size); } void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) { - if (opaque) opaque = 0; /* to make compiler happy */ + (void)opaque; _hfree(ptr); } @@ -306,7 +307,7 @@ voidpf ZLIB_INTERNAL zcalloc (opaque, items, size) unsigned items; unsigned size; { - if (opaque) items += size - size; /* make compiler happy */ + (void)opaque; return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : (voidpf)calloc(items, size); } @@ -315,8 +316,8 @@ void ZLIB_INTERNAL zcfree (opaque, ptr) voidpf opaque; voidpf ptr; { + (void)opaque; free(ptr); - if (opaque) return; /* make compiler happy */ } #endif /* MY_ZCALLOC */ diff --git a/contrib/zlib/zutil.h b/contrib/zlib/zutil.h index 24ab06b1c..60a0bca79 100644 --- a/contrib/zlib/zutil.h +++ b/contrib/zlib/zutil.h @@ -1,5 +1,5 @@ /* zutil.h -- internal interface and configuration of the compression library - * Copyright (C) 1995-2013 Jean-loup Gailly. + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -29,14 +29,12 @@ # include #endif -#ifdef Z_SOLO - typedef long ptrdiff_t; /* guess -- will be caught if guess is wrong */ -#endif - #ifndef local # define local static #endif -/* compile with -Dlocal if your debugger can't find static symbols */ +/* since "static" is used to mean two completely different things in C, we + define "local" for the non-static meaning of "static", for readability + (compile with -Dlocal if your debugger can't find static symbols) */ typedef unsigned char uch; typedef uch FAR uchf; @@ -98,28 +96,38 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ #endif #ifdef AMIGA -# define OS_CODE 0x01 +# define OS_CODE 1 #endif #if defined(VAXC) || defined(VMS) -# define OS_CODE 0x02 +# define OS_CODE 2 # define F_OPEN(name, mode) \ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") #endif +#ifdef __370__ +# if __TARGET_LIB__ < 0x20000000 +# define OS_CODE 4 +# elif __TARGET_LIB__ < 0x40000000 +# define OS_CODE 11 +# else +# define OS_CODE 8 +# endif +#endif + #if defined(ATARI) || defined(atarist) -# define OS_CODE 0x05 +# define OS_CODE 5 #endif #ifdef OS2 -# define OS_CODE 0x06 +# define OS_CODE 6 # if defined(M_I86) && !defined(Z_SOLO) # include # endif #endif #if defined(MACOS) || defined(TARGET_OS_MAC) -# define OS_CODE 0x07 +# define OS_CODE 7 # ifndef Z_SOLO # if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os # include /* for fdopen */ @@ -131,18 +139,24 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ # endif #endif -#ifdef TOPS20 -# define OS_CODE 0x0a +#ifdef __acorn +# define OS_CODE 13 #endif -#ifdef WIN32 -# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ -# define OS_CODE 0x0b -# endif +#if defined(WIN32) && !defined(__CYGWIN__) +# define OS_CODE 10 #endif -#ifdef __50SERIES /* Prime/PRIMOS */ -# define OS_CODE 0x0f +#ifdef _BEOS_ +# define OS_CODE 16 +#endif + +#ifdef __TOS_OS400__ +# define OS_CODE 18 +#endif + +#ifdef __APPLE__ +# define OS_CODE 19 #endif #if defined(_BEOS_) || defined(RISCOS) @@ -152,10 +166,6 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ #if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX # if defined(_WIN32_WCE) # define fdopen(fd,mode) NULL /* No fdopen() */ -# ifndef _PTRDIFF_T_DEFINED - typedef int ptrdiff_t; -# define _PTRDIFF_T_DEFINED -# endif # else # define fdopen(fd,type) _fdopen(fd,type) # endif @@ -177,7 +187,7 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ /* common defaults */ #ifndef OS_CODE -# define OS_CODE 0x03 /* assume Unix */ +# define OS_CODE 3 /* assume Unix */ #endif #ifndef F_OPEN @@ -216,7 +226,7 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ #endif /* Diagnostic functions */ -#ifdef DEBUG +#ifdef ZLIB_DEBUG # include extern int ZLIB_INTERNAL z_verbose; extern void ZLIB_INTERNAL z_error OF((char *m)); From f6706f3532fcccfd58579d1dc70e049c06aee697 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 4 Oct 2017 20:50:16 +0300 Subject: [PATCH 148/490] Travis: Build with clang too --- .travis.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.travis.yml b/.travis.yml index d59689f78..2eab656cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,10 @@ branches: os: - linux +compiler: + - gcc + - clang + env: global: # COVERITY_SCAN_TOKEN @@ -34,6 +38,10 @@ env: matrix: exclude: - os: linux + compiler: gcc + env: + - os: linux + compiler: clang env: include: @@ -49,6 +57,12 @@ matrix: - os: linux compiler: gcc env: LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF + - os: linux + compiler: clang + env: LINUX=1 TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=ON + - os: linux + compiler: clang + env: LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF install: - if [ $ANDROID ]; then wget -c http://dl.google.com/android/ndk/android-ndk-${PV}-${PLATF}.tar.bz2 && tar xf android-ndk-${PV}-${PLATF}.tar.bz2 ; fi From 9eeece1b356320a4ecbd96ebecac4dad464f5437 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 4 Oct 2017 21:10:49 +0300 Subject: [PATCH 149/490] Travis: Treat warnings as errors, without typos this time --- .travis.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.sh b/.travis.sh index f19c3a000..1ab1ee2b1 100755 --- a/.travis.sh +++ b/.travis.sh @@ -1,6 +1,6 @@ function generate() { - cmake -G "Unix Makefiles" -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -DBUILD_SHARED_LIBS=$SHARED_BUILD -DASSIMP_COVERALLS=$ENABLE_COVERALLS -DASSIMP_ERROR=ON -DASSIMP_ASAN=$ASAN + cmake -G "Unix Makefiles" -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -DBUILD_SHARED_LIBS=$SHARED_BUILD -DASSIMP_COVERALLS=$ENABLE_COVERALLS -DASSIMP_WERROR=ON -DASSIMP_ASAN=$ASAN } if [ $ANDROID ]; then From 452885672e314c7c0d2e938c454a00ada61877ac Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 4 Oct 2017 21:21:36 +0300 Subject: [PATCH 150/490] CMake: Remove OpenMP stuff, it's unused and breaks Travis clang build --- CMakeLists.txt | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c0fe062e..0aae76837 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,19 +151,6 @@ IF(ASSIMP_DOUBLE_PRECISION) ADD_DEFINITIONS(-DASSIMP_DOUBLE_PRECISION) ENDIF(ASSIMP_DOUBLE_PRECISION) -# Check for OpenMP support -find_package(OpenMP) -if (OPENMP_FOUND) - SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - - IF(MSVC) - IF(MSVC_VERSION GREATER 1910) - SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:twoPhase-") - ENDIF() - ENDIF() -endif() - CONFIGURE_FILE( ${CMAKE_CURRENT_LIST_DIR}/revision.h.in ${CMAKE_CURRENT_BINARY_DIR}/revision.h From 1ef3b0f3f3eb1d80bc787114f639eba750ddad2d Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 10:45:25 +0300 Subject: [PATCH 151/490] Fix warning about non-constant array size --- test/unit/TestModelFactory.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/TestModelFactory.h b/test/unit/TestModelFactory.h index f848f5536..ca070890d 100644 --- a/test/unit/TestModelFactory.h +++ b/test/unit/TestModelFactory.h @@ -89,7 +89,7 @@ public: scene->mRootNode = new aiNode; scene->mRootNode->mNumMeshes = 1; - scene->mRootNode->mMeshes = new unsigned int[scene->mRootNode->mNumMeshes]{ 0 }; + scene->mRootNode->mMeshes = new unsigned int[1]{ 0 }; return scene; } From e7c112916b585a480f162cd07a6361ff392b292e Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Thu, 5 Oct 2017 20:11:18 +1100 Subject: [PATCH 152/490] Removed unnecessary files from zlib contribution --- contrib/zlib/.gitignore | 26 - contrib/zlib/ChangeLog | 1518 ------------------------------------ contrib/zlib/FAQ | 368 --------- contrib/zlib/INDEX | 68 -- contrib/zlib/Makefile.in | 410 ---------- contrib/zlib/configure | 921 ---------------------- contrib/zlib/make_vms.com | 867 -------------------- contrib/zlib/treebuild.xml | 116 --- contrib/zlib/zlib.3 | 149 ---- contrib/zlib/zlib.3.pdf | Bin 19320 -> 0 bytes contrib/zlib/zlib.map | 94 --- contrib/zlib/zlib.pc.in | 13 - contrib/zlib/zlib2ansi | 152 ---- 13 files changed, 4702 deletions(-) delete mode 100644 contrib/zlib/.gitignore delete mode 100644 contrib/zlib/ChangeLog delete mode 100644 contrib/zlib/FAQ delete mode 100644 contrib/zlib/INDEX delete mode 100644 contrib/zlib/Makefile.in delete mode 100644 contrib/zlib/configure delete mode 100644 contrib/zlib/make_vms.com delete mode 100644 contrib/zlib/treebuild.xml delete mode 100644 contrib/zlib/zlib.3 delete mode 100644 contrib/zlib/zlib.3.pdf delete mode 100644 contrib/zlib/zlib.map delete mode 100644 contrib/zlib/zlib.pc.in delete mode 100644 contrib/zlib/zlib2ansi diff --git a/contrib/zlib/.gitignore b/contrib/zlib/.gitignore deleted file mode 100644 index b1c7422fe..000000000 --- a/contrib/zlib/.gitignore +++ /dev/null @@ -1,26 +0,0 @@ -*.diff -*.patch -*.orig -*.rej - -*~ -*.a -*.lo -*.o -*.dylib - -*.gcda -*.gcno -*.gcov - -/example -/example64 -/examplesh -/libz.so* -/minigzip -/minigzip64 -/minigzipsh -/zlib.pc -/configure.log - -.DS_Store diff --git a/contrib/zlib/ChangeLog b/contrib/zlib/ChangeLog deleted file mode 100644 index 9c6d95b8d..000000000 --- a/contrib/zlib/ChangeLog +++ /dev/null @@ -1,1518 +0,0 @@ - - ChangeLog file for zlib - -Changes in 1.2.11.1 (xx Jan 2017) -- - -Changes in 1.2.11 (15 Jan 2017) -- Fix deflate stored bug when pulling last block from window -- Permit immediate deflateParams changes before any deflate input - -Changes in 1.2.10 (2 Jan 2017) -- Avoid warnings on snprintf() return value -- Fix bug in deflate_stored() for zero-length input -- Fix bug in gzwrite.c that produced corrupt gzip files -- Remove files to be installed before copying them in Makefile.in -- Add warnings when compiling with assembler code - -Changes in 1.2.9 (31 Dec 2016) -- Fix contrib/minizip to permit unzipping with desktop API [Zouzou] -- Improve contrib/blast to return unused bytes -- Assure that gzoffset() is correct when appending -- Improve compress() and uncompress() to support large lengths -- Fix bug in test/example.c where error code not saved -- Remedy Coverity warning [Randers-Pehrson] -- Improve speed of gzprintf() in transparent mode -- Fix inflateInit2() bug when windowBits is 16 or 32 -- Change DEBUG macro to ZLIB_DEBUG -- Avoid uninitialized access by gzclose_w() -- Allow building zlib outside of the source directory -- Fix bug that accepted invalid zlib header when windowBits is zero -- Fix gzseek() problem on MinGW due to buggy _lseeki64 there -- Loop on write() calls in gzwrite.c in case of non-blocking I/O -- Add --warn (-w) option to ./configure for more compiler warnings -- Reject a window size of 256 bytes if not using the zlib wrapper -- Fix bug when level 0 used with Z_HUFFMAN or Z_RLE -- Add --debug (-d) option to ./configure to define ZLIB_DEBUG -- Fix bugs in creating a very large gzip header -- Add uncompress2() function, which returns the input size used -- Assure that deflateParams() will not switch functions mid-block -- Dramatically speed up deflation for level 0 (storing) -- Add gzfread(), duplicating the interface of fread() -- Add gzfwrite(), duplicating the interface of fwrite() -- Add deflateGetDictionary() function -- Use snprintf() for later versions of Microsoft C -- Fix *Init macros to use z_ prefix when requested -- Replace as400 with os400 for OS/400 support [Monnerat] -- Add crc32_z() and adler32_z() functions with size_t lengths -- Update Visual Studio project files [AraHaan] - -Changes in 1.2.8 (28 Apr 2013) -- Update contrib/minizip/iowin32.c for Windows RT [Vollant] -- Do not force Z_CONST for C++ -- Clean up contrib/vstudio [Roß] -- Correct spelling error in zlib.h -- Fix mixed line endings in contrib/vstudio - -Changes in 1.2.7.3 (13 Apr 2013) -- Fix version numbers and DLL names in contrib/vstudio/*/zlib.rc - -Changes in 1.2.7.2 (13 Apr 2013) -- Change check for a four-byte type back to hexadecimal -- Fix typo in win32/Makefile.msc -- Add casts in gzwrite.c for pointer differences - -Changes in 1.2.7.1 (24 Mar 2013) -- Replace use of unsafe string functions with snprintf if available -- Avoid including stddef.h on Windows for Z_SOLO compile [Niessink] -- Fix gzgetc undefine when Z_PREFIX set [Turk] -- Eliminate use of mktemp in Makefile (not always available) -- Fix bug in 'F' mode for gzopen() -- Add inflateGetDictionary() function -- Correct comment in deflate.h -- Use _snprintf for snprintf in Microsoft C -- On Darwin, only use /usr/bin/libtool if libtool is not Apple -- Delete "--version" file if created by "ar --version" [Richard G.] -- Fix configure check for veracity of compiler error return codes -- Fix CMake compilation of static lib for MSVC2010 x64 -- Remove unused variable in infback9.c -- Fix argument checks in gzlog_compress() and gzlog_write() -- Clean up the usage of z_const and respect const usage within zlib -- Clean up examples/gzlog.[ch] comparisons of different types -- Avoid shift equal to bits in type (caused endless loop) -- Fix uninitialized value bug in gzputc() introduced by const patches -- Fix memory allocation error in examples/zran.c [Nor] -- Fix bug where gzopen(), gzclose() would write an empty file -- Fix bug in gzclose() when gzwrite() runs out of memory -- Check for input buffer malloc failure in examples/gzappend.c -- Add note to contrib/blast to use binary mode in stdio -- Fix comparisons of differently signed integers in contrib/blast -- Check for invalid code length codes in contrib/puff -- Fix serious but very rare decompression bug in inftrees.c -- Update inflateBack() comments, since inflate() can be faster -- Use underscored I/O function names for WINAPI_FAMILY -- Add _tr_flush_bits to the external symbols prefixed by --zprefix -- Add contrib/vstudio/vc10 pre-build step for static only -- Quote --version-script argument in CMakeLists.txt -- Don't specify --version-script on Apple platforms in CMakeLists.txt -- Fix casting error in contrib/testzlib/testzlib.c -- Fix types in contrib/minizip to match result of get_crc_table() -- Simplify contrib/vstudio/vc10 with 'd' suffix -- Add TOP support to win32/Makefile.msc -- Suport i686 and amd64 assembler builds in CMakeLists.txt -- Fix typos in the use of _LARGEFILE64_SOURCE in zconf.h -- Add vc11 and vc12 build files to contrib/vstudio -- Add gzvprintf() as an undocumented function in zlib -- Fix configure for Sun shell -- Remove runtime check in configure for four-byte integer type -- Add casts and consts to ease user conversion to C++ -- Add man pages for minizip and miniunzip -- In Makefile uninstall, don't rm if preceding cd fails -- Do not return Z_BUF_ERROR if deflateParam() has nothing to write - -Changes in 1.2.7 (2 May 2012) -- Replace use of memmove() with a simple copy for portability -- Test for existence of strerror -- Restore gzgetc_ for backward compatibility with 1.2.6 -- Fix build with non-GNU make on Solaris -- Require gcc 4.0 or later on Mac OS X to use the hidden attribute -- Include unistd.h for Watcom C -- Use __WATCOMC__ instead of __WATCOM__ -- Do not use the visibility attribute if NO_VIZ defined -- Improve the detection of no hidden visibility attribute -- Avoid using __int64 for gcc or solo compilation -- Cast to char * in gzprintf to avoid warnings [Zinser] -- Fix make_vms.com for VAX [Zinser] -- Don't use library or built-in byte swaps -- Simplify test and use of gcc hidden attribute -- Fix bug in gzclose_w() when gzwrite() fails to allocate memory -- Add "x" (O_EXCL) and "e" (O_CLOEXEC) modes support to gzopen() -- Fix bug in test/minigzip.c for configure --solo -- Fix contrib/vstudio project link errors [Mohanathas] -- Add ability to choose the builder in make_vms.com [Schweda] -- Add DESTDIR support to mingw32 win32/Makefile.gcc -- Fix comments in win32/Makefile.gcc for proper usage -- Allow overriding the default install locations for cmake -- Generate and install the pkg-config file with cmake -- Build both a static and a shared version of zlib with cmake -- Include version symbols for cmake builds -- If using cmake with MSVC, add the source directory to the includes -- Remove unneeded EXTRA_CFLAGS from win32/Makefile.gcc [Truta] -- Move obsolete emx makefile to old [Truta] -- Allow the use of -Wundef when compiling or using zlib -- Avoid the use of the -u option with mktemp -- Improve inflate() documentation on the use of Z_FINISH -- Recognize clang as gcc -- Add gzopen_w() in Windows for wide character path names -- Rename zconf.h in CMakeLists.txt to move it out of the way -- Add source directory in CMakeLists.txt for building examples -- Look in build directory for zlib.pc in CMakeLists.txt -- Remove gzflags from zlibvc.def in vc9 and vc10 -- Fix contrib/minizip compilation in the MinGW environment -- Update ./configure for Solaris, support --64 [Mooney] -- Remove -R. from Solaris shared build (possible security issue) -- Avoid race condition for parallel make (-j) running example -- Fix type mismatch between get_crc_table() and crc_table -- Fix parsing of version with "-" in CMakeLists.txt [Snider, Ziegler] -- Fix the path to zlib.map in CMakeLists.txt -- Force the native libtool in Mac OS X to avoid GNU libtool [Beebe] -- Add instructions to win32/Makefile.gcc for shared install [Torri] - -Changes in 1.2.6.1 (12 Feb 2012) -- Avoid the use of the Objective-C reserved name "id" -- Include io.h in gzguts.h for Microsoft compilers -- Fix problem with ./configure --prefix and gzgetc macro -- Include gz_header definition when compiling zlib solo -- Put gzflags() functionality back in zutil.c -- Avoid library header include in crc32.c for Z_SOLO -- Use name in GCC_CLASSIC as C compiler for coverage testing, if set -- Minor cleanup in contrib/minizip/zip.c [Vollant] -- Update make_vms.com [Zinser] -- Remove unnecessary gzgetc_ function -- Use optimized byte swap operations for Microsoft and GNU [Snyder] -- Fix minor typo in zlib.h comments [Rzesniowiecki] - -Changes in 1.2.6 (29 Jan 2012) -- Update the Pascal interface in contrib/pascal -- Fix function numbers for gzgetc_ in zlibvc.def files -- Fix configure.ac for contrib/minizip [Schiffer] -- Fix large-entry detection in minizip on 64-bit systems [Schiffer] -- Have ./configure use the compiler return code for error indication -- Fix CMakeLists.txt for cross compilation [McClure] -- Fix contrib/minizip/zip.c for 64-bit architectures [Dalsnes] -- Fix compilation of contrib/minizip on FreeBSD [Marquez] -- Correct suggested usages in win32/Makefile.msc [Shachar, Horvath] -- Include io.h for Turbo C / Borland C on all platforms [Truta] -- Make version explicit in contrib/minizip/configure.ac [Bosmans] -- Avoid warning for no encryption in contrib/minizip/zip.c [Vollant] -- Minor cleanup up contrib/minizip/unzip.c [Vollant] -- Fix bug when compiling minizip with C++ [Vollant] -- Protect for long name and extra fields in contrib/minizip [Vollant] -- Avoid some warnings in contrib/minizip [Vollant] -- Add -I../.. -L../.. to CFLAGS for minizip and miniunzip -- Add missing libs to minizip linker command -- Add support for VPATH builds in contrib/minizip -- Add an --enable-demos option to contrib/minizip/configure -- Add the generation of configure.log by ./configure -- Exit when required parameters not provided to win32/Makefile.gcc -- Have gzputc return the character written instead of the argument -- Use the -m option on ldconfig for BSD systems [Tobias] -- Correct in zlib.map when deflateResetKeep was added - -Changes in 1.2.5.3 (15 Jan 2012) -- Restore gzgetc function for binary compatibility -- Do not use _lseeki64 under Borland C++ [Truta] -- Update win32/Makefile.msc to build test/*.c [Truta] -- Remove old/visualc6 given CMakefile and other alternatives -- Update AS400 build files and documentation [Monnerat] -- Update win32/Makefile.gcc to build test/*.c [Truta] -- Permit stronger flushes after Z_BLOCK flushes -- Avoid extraneous empty blocks when doing empty flushes -- Permit Z_NULL arguments to deflatePending -- Allow deflatePrime() to insert bits in the middle of a stream -- Remove second empty static block for Z_PARTIAL_FLUSH -- Write out all of the available bits when using Z_BLOCK -- Insert the first two strings in the hash table after a flush - -Changes in 1.2.5.2 (17 Dec 2011) -- fix ld error: unable to find version dependency 'ZLIB_1.2.5' -- use relative symlinks for shared libs -- Avoid searching past window for Z_RLE strategy -- Assure that high-water mark initialization is always applied in deflate -- Add assertions to fill_window() in deflate.c to match comments -- Update python link in README -- Correct spelling error in gzread.c -- Fix bug in gzgets() for a concatenated empty gzip stream -- Correct error in comment for gz_make() -- Change gzread() and related to ignore junk after gzip streams -- Allow gzread() and related to continue after gzclearerr() -- Allow gzrewind() and gzseek() after a premature end-of-file -- Simplify gzseek() now that raw after gzip is ignored -- Change gzgetc() to a macro for speed (~40% speedup in testing) -- Fix gzclose() to return the actual error last encountered -- Always add large file support for windows -- Include zconf.h for windows large file support -- Include zconf.h.cmakein for windows large file support -- Update zconf.h.cmakein on make distclean -- Merge vestigial vsnprintf determination from zutil.h to gzguts.h -- Clarify how gzopen() appends in zlib.h comments -- Correct documentation of gzdirect() since junk at end now ignored -- Add a transparent write mode to gzopen() when 'T' is in the mode -- Update python link in zlib man page -- Get inffixed.h and MAKEFIXED result to match -- Add a ./config --solo option to make zlib subset with no library use -- Add undocumented inflateResetKeep() function for CAB file decoding -- Add --cover option to ./configure for gcc coverage testing -- Add #define ZLIB_CONST option to use const in the z_stream interface -- Add comment to gzdopen() in zlib.h to use dup() when using fileno() -- Note behavior of uncompress() to provide as much data as it can -- Add files in contrib/minizip to aid in building libminizip -- Split off AR options in Makefile.in and configure -- Change ON macro to Z_ARG to avoid application conflicts -- Facilitate compilation with Borland C++ for pragmas and vsnprintf -- Include io.h for Turbo C / Borland C++ -- Move example.c and minigzip.c to test/ -- Simplify incomplete code table filling in inflate_table() -- Remove code from inflate.c and infback.c that is impossible to execute -- Test the inflate code with full coverage -- Allow deflateSetDictionary, inflateSetDictionary at any time (in raw) -- Add deflateResetKeep and fix inflateResetKeep to retain dictionary -- Fix gzwrite.c to accommodate reduced memory zlib compilation -- Have inflate() with Z_FINISH avoid the allocation of a window -- Do not set strm->adler when doing raw inflate -- Fix gzeof() to behave just like feof() when read is not past end of file -- Fix bug in gzread.c when end-of-file is reached -- Avoid use of Z_BUF_ERROR in gz* functions except for premature EOF -- Document gzread() capability to read concurrently written files -- Remove hard-coding of resource compiler in CMakeLists.txt [Blammo] - -Changes in 1.2.5.1 (10 Sep 2011) -- Update FAQ entry on shared builds (#13) -- Avoid symbolic argument to chmod in Makefile.in -- Fix bug and add consts in contrib/puff [Oberhumer] -- Update contrib/puff/zeros.raw test file to have all block types -- Add full coverage test for puff in contrib/puff/Makefile -- Fix static-only-build install in Makefile.in -- Fix bug in unzGetCurrentFileInfo() in contrib/minizip [Kuno] -- Add libz.a dependency to shared in Makefile.in for parallel builds -- Spell out "number" (instead of "nb") in zlib.h for total_in, total_out -- Replace $(...) with `...` in configure for non-bash sh [Bowler] -- Add darwin* to Darwin* and solaris* to SunOS\ 5* in configure [Groffen] -- Add solaris* to Linux* in configure to allow gcc use [Groffen] -- Add *bsd* to Linux* case in configure [Bar-Lev] -- Add inffast.obj to dependencies in win32/Makefile.msc -- Correct spelling error in deflate.h [Kohler] -- Change libzdll.a again to libz.dll.a (!) in win32/Makefile.gcc -- Add test to configure for GNU C looking for gcc in output of $cc -v -- Add zlib.pc generation to win32/Makefile.gcc [Weigelt] -- Fix bug in zlib.h for _FILE_OFFSET_BITS set and _LARGEFILE64_SOURCE not -- Add comment in zlib.h that adler32_combine with len2 < 0 makes no sense -- Make NO_DIVIDE option in adler32.c much faster (thanks to John Reiser) -- Make stronger test in zconf.h to include unistd.h for LFS -- Apply Darwin patches for 64-bit file offsets to contrib/minizip [Slack] -- Fix zlib.h LFS support when Z_PREFIX used -- Add updated as400 support (removed from old) [Monnerat] -- Avoid deflate sensitivity to volatile input data -- Avoid division in adler32_combine for NO_DIVIDE -- Clarify the use of Z_FINISH with deflateBound() amount of space -- Set binary for output file in puff.c -- Use u4 type for crc_table to avoid conversion warnings -- Apply casts in zlib.h to avoid conversion warnings -- Add OF to prototypes for adler32_combine_ and crc32_combine_ [Miller] -- Improve inflateSync() documentation to note indeterminancy -- Add deflatePending() function to return the amount of pending output -- Correct the spelling of "specification" in FAQ [Randers-Pehrson] -- Add a check in configure for stdarg.h, use for gzprintf() -- Check that pointers fit in ints when gzprint() compiled old style -- Add dummy name before $(SHAREDLIBV) in Makefile [Bar-Lev, Bowler] -- Delete line in configure that adds -L. libz.a to LDFLAGS [Weigelt] -- Add debug records in assmebler code [Londer] -- Update RFC references to use http://tools.ietf.org/html/... [Li] -- Add --archs option, use of libtool to configure for Mac OS X [Borstel] - -Changes in 1.2.5 (19 Apr 2010) -- Disable visibility attribute in win32/Makefile.gcc [Bar-Lev] -- Default to libdir as sharedlibdir in configure [Nieder] -- Update copyright dates on modified source files -- Update trees.c to be able to generate modified trees.h -- Exit configure for MinGW, suggesting win32/Makefile.gcc -- Check for NULL path in gz_open [Homurlu] - -Changes in 1.2.4.5 (18 Apr 2010) -- Set sharedlibdir in configure [Torok] -- Set LDFLAGS in Makefile.in [Bar-Lev] -- Avoid mkdir objs race condition in Makefile.in [Bowler] -- Add ZLIB_INTERNAL in front of internal inter-module functions and arrays -- Define ZLIB_INTERNAL to hide internal functions and arrays for GNU C -- Don't use hidden attribute when it is a warning generator (e.g. Solaris) - -Changes in 1.2.4.4 (18 Apr 2010) -- Fix CROSS_PREFIX executable testing, CHOST extract, mingw* [Torok] -- Undefine _LARGEFILE64_SOURCE in zconf.h if it is zero, but not if empty -- Try to use bash or ksh regardless of functionality of /bin/sh -- Fix configure incompatibility with NetBSD sh -- Remove attempt to run under bash or ksh since have better NetBSD fix -- Fix win32/Makefile.gcc for MinGW [Bar-Lev] -- Add diagnostic messages when using CROSS_PREFIX in configure -- Added --sharedlibdir option to configure [Weigelt] -- Use hidden visibility attribute when available [Frysinger] - -Changes in 1.2.4.3 (10 Apr 2010) -- Only use CROSS_PREFIX in configure for ar and ranlib if they exist -- Use CROSS_PREFIX for nm [Bar-Lev] -- Assume _LARGEFILE64_SOURCE defined is equivalent to true -- Avoid use of undefined symbols in #if with && and || -- Make *64 prototypes in gzguts.h consistent with functions -- Add -shared load option for MinGW in configure [Bowler] -- Move z_off64_t to public interface, use instead of off64_t -- Remove ! from shell test in configure (not portable to Solaris) -- Change +0 macro tests to -0 for possibly increased portability - -Changes in 1.2.4.2 (9 Apr 2010) -- Add consistent carriage returns to readme.txt's in masmx86 and masmx64 -- Really provide prototypes for *64 functions when building without LFS -- Only define unlink() in minigzip.c if unistd.h not included -- Update README to point to contrib/vstudio project files -- Move projects/vc6 to old/ and remove projects/ -- Include stdlib.h in minigzip.c for setmode() definition under WinCE -- Clean up assembler builds in win32/Makefile.msc [Rowe] -- Include sys/types.h for Microsoft for off_t definition -- Fix memory leak on error in gz_open() -- Symbolize nm as $NM in configure [Weigelt] -- Use TEST_LDSHARED instead of LDSHARED to link test programs [Weigelt] -- Add +0 to _FILE_OFFSET_BITS and _LFS64_LARGEFILE in case not defined -- Fix bug in gzeof() to take into account unused input data -- Avoid initialization of structures with variables in puff.c -- Updated win32/README-WIN32.txt [Rowe] - -Changes in 1.2.4.1 (28 Mar 2010) -- Remove the use of [a-z] constructs for sed in configure [gentoo 310225] -- Remove $(SHAREDLIB) from LIBS in Makefile.in [Creech] -- Restore "for debugging" comment on sprintf() in gzlib.c -- Remove fdopen for MVS from gzguts.h -- Put new README-WIN32.txt in win32 [Rowe] -- Add check for shell to configure and invoke another shell if needed -- Fix big fat stinking bug in gzseek() on uncompressed files -- Remove vestigial F_OPEN64 define in zutil.h -- Set and check the value of _LARGEFILE_SOURCE and _LARGEFILE64_SOURCE -- Avoid errors on non-LFS systems when applications define LFS macros -- Set EXE to ".exe" in configure for MINGW [Kahle] -- Match crc32() in crc32.c exactly to the prototype in zlib.h [Sherrill] -- Add prefix for cross-compilation in win32/makefile.gcc [Bar-Lev] -- Add DLL install in win32/makefile.gcc [Bar-Lev] -- Allow Linux* or linux* from uname in configure [Bar-Lev] -- Allow ldconfig to be redefined in configure and Makefile.in [Bar-Lev] -- Add cross-compilation prefixes to configure [Bar-Lev] -- Match type exactly in gz_load() invocation in gzread.c -- Match type exactly of zcalloc() in zutil.c to zlib.h alloc_func -- Provide prototypes for *64 functions when building zlib without LFS -- Don't use -lc when linking shared library on MinGW -- Remove errno.h check in configure and vestigial errno code in zutil.h - -Changes in 1.2.4 (14 Mar 2010) -- Fix VER3 extraction in configure for no fourth subversion -- Update zlib.3, add docs to Makefile.in to make .pdf out of it -- Add zlib.3.pdf to distribution -- Don't set error code in gzerror() if passed pointer is NULL -- Apply destination directory fixes to CMakeLists.txt [Lowman] -- Move #cmakedefine's to a new zconf.in.cmakein -- Restore zconf.h for builds that don't use configure or cmake -- Add distclean to dummy Makefile for convenience -- Update and improve INDEX, README, and FAQ -- Update CMakeLists.txt for the return of zconf.h [Lowman] -- Update contrib/vstudio/vc9 and vc10 [Vollant] -- Change libz.dll.a back to libzdll.a in win32/Makefile.gcc -- Apply license and readme changes to contrib/asm686 [Raiter] -- Check file name lengths and add -c option in minigzip.c [Li] -- Update contrib/amd64 and contrib/masmx86/ [Vollant] -- Avoid use of "eof" parameter in trees.c to not shadow library variable -- Update make_vms.com for removal of zlibdefs.h [Zinser] -- Update assembler code and vstudio projects in contrib [Vollant] -- Remove outdated assembler code contrib/masm686 and contrib/asm586 -- Remove old vc7 and vc8 from contrib/vstudio -- Update win32/Makefile.msc, add ZLIB_VER_SUBREVISION [Rowe] -- Fix memory leaks in gzclose_r() and gzclose_w(), file leak in gz_open() -- Add contrib/gcc_gvmat64 for longest_match and inflate_fast [Vollant] -- Remove *64 functions from win32/zlib.def (they're not 64-bit yet) -- Fix bug in void-returning vsprintf() case in gzwrite.c -- Fix name change from inflate.h in contrib/inflate86/inffas86.c -- Check if temporary file exists before removing in make_vms.com [Zinser] -- Fix make install and uninstall for --static option -- Fix usage of _MSC_VER in gzguts.h and zutil.h [Truta] -- Update readme.txt in contrib/masmx64 and masmx86 to assemble - -Changes in 1.2.3.9 (21 Feb 2010) -- Expunge gzio.c -- Move as400 build information to old -- Fix updates in contrib/minizip and contrib/vstudio -- Add const to vsnprintf test in configure to avoid warnings [Weigelt] -- Delete zconf.h (made by configure) [Weigelt] -- Change zconf.in.h to zconf.h.in per convention [Weigelt] -- Check for NULL buf in gzgets() -- Return empty string for gzgets() with len == 1 (like fgets()) -- Fix description of gzgets() in zlib.h for end-of-file, NULL return -- Update minizip to 1.1 [Vollant] -- Avoid MSVC loss of data warnings in gzread.c, gzwrite.c -- Note in zlib.h that gzerror() should be used to distinguish from EOF -- Remove use of snprintf() from gzlib.c -- Fix bug in gzseek() -- Update contrib/vstudio, adding vc9 and vc10 [Kuno, Vollant] -- Fix zconf.h generation in CMakeLists.txt [Lowman] -- Improve comments in zconf.h where modified by configure - -Changes in 1.2.3.8 (13 Feb 2010) -- Clean up text files (tabs, trailing whitespace, etc.) [Oberhumer] -- Use z_off64_t in gz_zero() and gz_skip() to match state->skip -- Avoid comparison problem when sizeof(int) == sizeof(z_off64_t) -- Revert to Makefile.in from 1.2.3.6 (live with the clutter) -- Fix missing error return in gzflush(), add zlib.h note -- Add *64 functions to zlib.map [Levin] -- Fix signed/unsigned comparison in gz_comp() -- Use SFLAGS when testing shared linking in configure -- Add --64 option to ./configure to use -m64 with gcc -- Fix ./configure --help to correctly name options -- Have make fail if a test fails [Levin] -- Avoid buffer overrun in contrib/masmx64/gvmat64.asm [Simpson] -- Remove assembler object files from contrib - -Changes in 1.2.3.7 (24 Jan 2010) -- Always gzopen() with O_LARGEFILE if available -- Fix gzdirect() to work immediately after gzopen() or gzdopen() -- Make gzdirect() more precise when the state changes while reading -- Improve zlib.h documentation in many places -- Catch memory allocation failure in gz_open() -- Complete close operation if seek forward in gzclose_w() fails -- Return Z_ERRNO from gzclose_r() if close() fails -- Return Z_STREAM_ERROR instead of EOF for gzclose() being passed NULL -- Return zero for gzwrite() errors to match zlib.h description -- Return -1 on gzputs() error to match zlib.h description -- Add zconf.in.h to allow recovery from configure modification [Weigelt] -- Fix static library permissions in Makefile.in [Weigelt] -- Avoid warnings in configure tests that hide functionality [Weigelt] -- Add *BSD and DragonFly to Linux case in configure [gentoo 123571] -- Change libzdll.a to libz.dll.a in win32/Makefile.gcc [gentoo 288212] -- Avoid access of uninitialized data for first inflateReset2 call [Gomes] -- Keep object files in subdirectories to reduce the clutter somewhat -- Remove default Makefile and zlibdefs.h, add dummy Makefile -- Add new external functions to Z_PREFIX, remove duplicates, z_z_ -> z_ -- Remove zlibdefs.h completely -- modify zconf.h instead - -Changes in 1.2.3.6 (17 Jan 2010) -- Avoid void * arithmetic in gzread.c and gzwrite.c -- Make compilers happier with const char * for gz_error message -- Avoid unused parameter warning in inflate.c -- Avoid signed-unsigned comparison warning in inflate.c -- Indent #pragma's for traditional C -- Fix usage of strwinerror() in glib.c, change to gz_strwinerror() -- Correct email address in configure for system options -- Update make_vms.com and add make_vms.com to contrib/minizip [Zinser] -- Update zlib.map [Brown] -- Fix Makefile.in for Solaris 10 make of example64 and minizip64 [Torok] -- Apply various fixes to CMakeLists.txt [Lowman] -- Add checks on len in gzread() and gzwrite() -- Add error message for no more room for gzungetc() -- Remove zlib version check in gzwrite() -- Defer compression of gzprintf() result until need to -- Use snprintf() in gzdopen() if available -- Remove USE_MMAP configuration determination (only used by minigzip) -- Remove examples/pigz.c (available separately) -- Update examples/gun.c to 1.6 - -Changes in 1.2.3.5 (8 Jan 2010) -- Add space after #if in zutil.h for some compilers -- Fix relatively harmless bug in deflate_fast() [Exarevsky] -- Fix same problem in deflate_slow() -- Add $(SHAREDLIBV) to LIBS in Makefile.in [Brown] -- Add deflate_rle() for faster Z_RLE strategy run-length encoding -- Add deflate_huff() for faster Z_HUFFMAN_ONLY encoding -- Change name of "write" variable in inffast.c to avoid library collisions -- Fix premature EOF from gzread() in gzio.c [Brown] -- Use zlib header window size if windowBits is 0 in inflateInit2() -- Remove compressBound() call in deflate.c to avoid linking compress.o -- Replace use of errno in gz* with functions, support WinCE [Alves] -- Provide alternative to perror() in minigzip.c for WinCE [Alves] -- Don't use _vsnprintf on later versions of MSVC [Lowman] -- Add CMake build script and input file [Lowman] -- Update contrib/minizip to 1.1 [Svensson, Vollant] -- Moved nintendods directory from contrib to . -- Replace gzio.c with a new set of routines with the same functionality -- Add gzbuffer(), gzoffset(), gzclose_r(), gzclose_w() as part of above -- Update contrib/minizip to 1.1b -- Change gzeof() to return 0 on error instead of -1 to agree with zlib.h - -Changes in 1.2.3.4 (21 Dec 2009) -- Use old school .SUFFIXES in Makefile.in for FreeBSD compatibility -- Update comments in configure and Makefile.in for default --shared -- Fix test -z's in configure [Marquess] -- Build examplesh and minigzipsh when not testing -- Change NULL's to Z_NULL's in deflate.c and in comments in zlib.h -- Import LDFLAGS from the environment in configure -- Fix configure to populate SFLAGS with discovered CFLAGS options -- Adapt make_vms.com to the new Makefile.in [Zinser] -- Add zlib2ansi script for C++ compilation [Marquess] -- Add _FILE_OFFSET_BITS=64 test to make test (when applicable) -- Add AMD64 assembler code for longest match to contrib [Teterin] -- Include options from $SFLAGS when doing $LDSHARED -- Simplify 64-bit file support by introducing z_off64_t type -- Make shared object files in objs directory to work around old Sun cc -- Use only three-part version number for Darwin shared compiles -- Add rc option to ar in Makefile.in for when ./configure not run -- Add -WI,-rpath,. to LDFLAGS for OSF 1 V4* -- Set LD_LIBRARYN32_PATH for SGI IRIX shared compile -- Protect against _FILE_OFFSET_BITS being defined when compiling zlib -- Rename Makefile.in targets allstatic to static and allshared to shared -- Fix static and shared Makefile.in targets to be independent -- Correct error return bug in gz_open() by setting state [Brown] -- Put spaces before ;;'s in configure for better sh compatibility -- Add pigz.c (parallel implementation of gzip) to examples/ -- Correct constant in crc32.c to UL [Leventhal] -- Reject negative lengths in crc32_combine() -- Add inflateReset2() function to work like inflateEnd()/inflateInit2() -- Include sys/types.h for _LARGEFILE64_SOURCE [Brown] -- Correct typo in doc/algorithm.txt [Janik] -- Fix bug in adler32_combine() [Zhu] -- Catch missing-end-of-block-code error in all inflates and in puff - Assures that random input to inflate eventually results in an error -- Added enough.c (calculation of ENOUGH for inftrees.h) to examples/ -- Update ENOUGH and its usage to reflect discovered bounds -- Fix gzerror() error report on empty input file [Brown] -- Add ush casts in trees.c to avoid pedantic runtime errors -- Fix typo in zlib.h uncompress() description [Reiss] -- Correct inflate() comments with regard to automatic header detection -- Remove deprecation comment on Z_PARTIAL_FLUSH (it stays) -- Put new version of gzlog (2.0) in examples with interruption recovery -- Add puff compile option to permit invalid distance-too-far streams -- Add puff TEST command options, ability to read piped input -- Prototype the *64 functions in zlib.h when _FILE_OFFSET_BITS == 64, but - _LARGEFILE64_SOURCE not defined -- Fix Z_FULL_FLUSH to truly erase the past by resetting s->strstart -- Fix deflateSetDictionary() to use all 32K for output consistency -- Remove extraneous #define MIN_LOOKAHEAD in deflate.c (in deflate.h) -- Clear bytes after deflate lookahead to avoid use of uninitialized data -- Change a limit in inftrees.c to be more transparent to Coverity Prevent -- Update win32/zlib.def with exported symbols from zlib.h -- Correct spelling errors in zlib.h [Willem, Sobrado] -- Allow Z_BLOCK for deflate() to force a new block -- Allow negative bits in inflatePrime() to delete existing bit buffer -- Add Z_TREES flush option to inflate() to return at end of trees -- Add inflateMark() to return current state information for random access -- Add Makefile for NintendoDS to contrib [Costa] -- Add -w in configure compile tests to avoid spurious warnings [Beucler] -- Fix typos in zlib.h comments for deflateSetDictionary() -- Fix EOF detection in transparent gzread() [Maier] - -Changes in 1.2.3.3 (2 October 2006) -- Make --shared the default for configure, add a --static option -- Add compile option to permit invalid distance-too-far streams -- Add inflateUndermine() function which is required to enable above -- Remove use of "this" variable name for C++ compatibility [Marquess] -- Add testing of shared library in make test, if shared library built -- Use ftello() and fseeko() if available instead of ftell() and fseek() -- Provide two versions of all functions that use the z_off_t type for - binary compatibility -- a normal version and a 64-bit offset version, - per the Large File Support Extension when _LARGEFILE64_SOURCE is - defined; use the 64-bit versions by default when _FILE_OFFSET_BITS - is defined to be 64 -- Add a --uname= option to configure to perhaps help with cross-compiling - -Changes in 1.2.3.2 (3 September 2006) -- Turn off silly Borland warnings [Hay] -- Use off64_t and define _LARGEFILE64_SOURCE when present -- Fix missing dependency on inffixed.h in Makefile.in -- Rig configure --shared to build both shared and static [Teredesai, Truta] -- Remove zconf.in.h and instead create a new zlibdefs.h file -- Fix contrib/minizip/unzip.c non-encrypted after encrypted [Vollant] -- Add treebuild.xml (see http://treebuild.metux.de/) [Weigelt] - -Changes in 1.2.3.1 (16 August 2006) -- Add watcom directory with OpenWatcom make files [Daniel] -- Remove #undef of FAR in zconf.in.h for MVS [Fedtke] -- Update make_vms.com [Zinser] -- Use -fPIC for shared build in configure [Teredesai, Nicholson] -- Use only major version number for libz.so on IRIX and OSF1 [Reinholdtsen] -- Use fdopen() (not _fdopen()) for Interix in zutil.h [Bäck] -- Add some FAQ entries about the contrib directory -- Update the MVS question in the FAQ -- Avoid extraneous reads after EOF in gzio.c [Brown] -- Correct spelling of "successfully" in gzio.c [Randers-Pehrson] -- Add comments to zlib.h about gzerror() usage [Brown] -- Set extra flags in gzip header in gzopen() like deflate() does -- Make configure options more compatible with double-dash conventions - [Weigelt] -- Clean up compilation under Solaris SunStudio cc [Rowe, Reinholdtsen] -- Fix uninstall target in Makefile.in [Truta] -- Add pkgconfig support [Weigelt] -- Use $(DESTDIR) macro in Makefile.in [Reinholdtsen, Weigelt] -- Replace set_data_type() with a more accurate detect_data_type() in - trees.c, according to the txtvsbin.txt document [Truta] -- Swap the order of #include and #include "zlib.h" in - gzio.c, example.c and minigzip.c [Truta] -- Shut up annoying VS2005 warnings about standard C deprecation [Rowe, - Truta] (where?) -- Fix target "clean" from win32/Makefile.bor [Truta] -- Create .pdb and .manifest files in win32/makefile.msc [Ziegler, Rowe] -- Update zlib www home address in win32/DLL_FAQ.txt [Truta] -- Update contrib/masmx86/inffas32.asm for VS2005 [Vollant, Van Wassenhove] -- Enable browse info in the "Debug" and "ASM Debug" configurations in - the Visual C++ 6 project, and set (non-ASM) "Debug" as default [Truta] -- Add pkgconfig support [Weigelt] -- Add ZLIB_VER_MAJOR, ZLIB_VER_MINOR and ZLIB_VER_REVISION in zlib.h, - for use in win32/zlib1.rc [Polushin, Rowe, Truta] -- Add a document that explains the new text detection scheme to - doc/txtvsbin.txt [Truta] -- Add rfc1950.txt, rfc1951.txt and rfc1952.txt to doc/ [Truta] -- Move algorithm.txt into doc/ [Truta] -- Synchronize FAQ with website -- Fix compressBound(), was low for some pathological cases [Fearnley] -- Take into account wrapper variations in deflateBound() -- Set examples/zpipe.c input and output to binary mode for Windows -- Update examples/zlib_how.html with new zpipe.c (also web site) -- Fix some warnings in examples/gzlog.c and examples/zran.c (it seems - that gcc became pickier in 4.0) -- Add zlib.map for Linux: "All symbols from zlib-1.1.4 remain - un-versioned, the patch adds versioning only for symbols introduced in - zlib-1.2.0 or later. It also declares as local those symbols which are - not designed to be exported." [Levin] -- Update Z_PREFIX list in zconf.in.h, add --zprefix option to configure -- Do not initialize global static by default in trees.c, add a response - NO_INIT_GLOBAL_POINTERS to initialize them if needed [Marquess] -- Don't use strerror() in gzio.c under WinCE [Yakimov] -- Don't use errno.h in zutil.h under WinCE [Yakimov] -- Move arguments for AR to its usage to allow replacing ar [Marot] -- Add HAVE_VISIBILITY_PRAGMA in zconf.in.h for Mozilla [Randers-Pehrson] -- Improve inflateInit() and inflateInit2() documentation -- Fix structure size comment in inflate.h -- Change configure help option from --h* to --help [Santos] - -Changes in 1.2.3 (18 July 2005) -- Apply security vulnerability fixes to contrib/infback9 as well -- Clean up some text files (carriage returns, trailing space) -- Update testzlib, vstudio, masmx64, and masmx86 in contrib [Vollant] - -Changes in 1.2.2.4 (11 July 2005) -- Add inflatePrime() function for starting inflation at bit boundary -- Avoid some Visual C warnings in deflate.c -- Avoid more silly Visual C warnings in inflate.c and inftrees.c for 64-bit - compile -- Fix some spelling errors in comments [Betts] -- Correct inflateInit2() error return documentation in zlib.h -- Add zran.c example of compressed data random access to examples - directory, shows use of inflatePrime() -- Fix cast for assignments to strm->state in inflate.c and infback.c -- Fix zlibCompileFlags() in zutil.c to use 1L for long shifts [Oberhumer] -- Move declarations of gf2 functions to right place in crc32.c [Oberhumer] -- Add cast in trees.c t avoid a warning [Oberhumer] -- Avoid some warnings in fitblk.c, gun.c, gzjoin.c in examples [Oberhumer] -- Update make_vms.com [Zinser] -- Initialize state->write in inflateReset() since copied in inflate_fast() -- Be more strict on incomplete code sets in inflate_table() and increase - ENOUGH and MAXD -- this repairs a possible security vulnerability for - invalid inflate input. Thanks to Tavis Ormandy and Markus Oberhumer for - discovering the vulnerability and providing test cases. -- Add ia64 support to configure for HP-UX [Smith] -- Add error return to gzread() for format or i/o error [Levin] -- Use malloc.h for OS/2 [Necasek] - -Changes in 1.2.2.3 (27 May 2005) -- Replace 1U constants in inflate.c and inftrees.c for 64-bit compile -- Typecast fread() return values in gzio.c [Vollant] -- Remove trailing space in minigzip.c outmode (VC++ can't deal with it) -- Fix crc check bug in gzread() after gzungetc() [Heiner] -- Add the deflateTune() function to adjust internal compression parameters -- Add a fast gzip decompressor, gun.c, to examples (use of inflateBack) -- Remove an incorrect assertion in examples/zpipe.c -- Add C++ wrapper in infback9.h [Donais] -- Fix bug in inflateCopy() when decoding fixed codes -- Note in zlib.h how much deflateSetDictionary() actually uses -- Remove USE_DICT_HEAD in deflate.c (would mess up inflate if used) -- Add _WIN32_WCE to define WIN32 in zconf.in.h [Spencer] -- Don't include stderr.h or errno.h for _WIN32_WCE in zutil.h [Spencer] -- Add gzdirect() function to indicate transparent reads -- Update contrib/minizip [Vollant] -- Fix compilation of deflate.c when both ASMV and FASTEST [Oberhumer] -- Add casts in crc32.c to avoid warnings [Oberhumer] -- Add contrib/masmx64 [Vollant] -- Update contrib/asm586, asm686, masmx86, testzlib, vstudio [Vollant] - -Changes in 1.2.2.2 (30 December 2004) -- Replace structure assignments in deflate.c and inflate.c with zmemcpy to - avoid implicit memcpy calls (portability for no-library compilation) -- Increase sprintf() buffer size in gzdopen() to allow for large numbers -- Add INFLATE_STRICT to check distances against zlib header -- Improve WinCE errno handling and comments [Chang] -- Remove comment about no gzip header processing in FAQ -- Add Z_FIXED strategy option to deflateInit2() to force fixed trees -- Add updated make_vms.com [Coghlan], update README -- Create a new "examples" directory, move gzappend.c there, add zpipe.c, - fitblk.c, gzlog.[ch], gzjoin.c, and zlib_how.html. -- Add FAQ entry and comments in deflate.c on uninitialized memory access -- Add Solaris 9 make options in configure [Gilbert] -- Allow strerror() usage in gzio.c for STDC -- Fix DecompressBuf in contrib/delphi/ZLib.pas [ManChesTer] -- Update contrib/masmx86/inffas32.asm and gvmat32.asm [Vollant] -- Use z_off_t for adler32_combine() and crc32_combine() lengths -- Make adler32() much faster for small len -- Use OS_CODE in deflate() default gzip header - -Changes in 1.2.2.1 (31 October 2004) -- Allow inflateSetDictionary() call for raw inflate -- Fix inflate header crc check bug for file names and comments -- Add deflateSetHeader() and gz_header structure for custom gzip headers -- Add inflateGetheader() to retrieve gzip headers -- Add crc32_combine() and adler32_combine() functions -- Add alloc_func, free_func, in_func, out_func to Z_PREFIX list -- Use zstreamp consistently in zlib.h (inflate_back functions) -- Remove GUNZIP condition from definition of inflate_mode in inflate.h - and in contrib/inflate86/inffast.S [Truta, Anderson] -- Add support for AMD64 in contrib/inflate86/inffas86.c [Anderson] -- Update projects/README.projects and projects/visualc6 [Truta] -- Update win32/DLL_FAQ.txt [Truta] -- Avoid warning under NO_GZCOMPRESS in gzio.c; fix typo [Truta] -- Deprecate Z_ASCII; use Z_TEXT instead [Truta] -- Use a new algorithm for setting strm->data_type in trees.c [Truta] -- Do not define an exit() prototype in zutil.c unless DEBUG defined -- Remove prototype of exit() from zutil.c, example.c, minigzip.c [Truta] -- Add comment in zlib.h for Z_NO_FLUSH parameter to deflate() -- Fix Darwin build version identification [Peterson] - -Changes in 1.2.2 (3 October 2004) -- Update zlib.h comments on gzip in-memory processing -- Set adler to 1 in inflateReset() to support Java test suite [Walles] -- Add contrib/dotzlib [Ravn] -- Update win32/DLL_FAQ.txt [Truta] -- Update contrib/minizip [Vollant] -- Move contrib/visual-basic.txt to old/ [Truta] -- Fix assembler builds in projects/visualc6/ [Truta] - -Changes in 1.2.1.2 (9 September 2004) -- Update INDEX file -- Fix trees.c to update strm->data_type (no one ever noticed!) -- Fix bug in error case in inflate.c, infback.c, and infback9.c [Brown] -- Add "volatile" to crc table flag declaration (for DYNAMIC_CRC_TABLE) -- Add limited multitasking protection to DYNAMIC_CRC_TABLE -- Add NO_vsnprintf for VMS in zutil.h [Mozilla] -- Don't declare strerror() under VMS [Mozilla] -- Add comment to DYNAMIC_CRC_TABLE to use get_crc_table() to initialize -- Update contrib/ada [Anisimkov] -- Update contrib/minizip [Vollant] -- Fix configure to not hardcode directories for Darwin [Peterson] -- Fix gzio.c to not return error on empty files [Brown] -- Fix indentation; update version in contrib/delphi/ZLib.pas and - contrib/pascal/zlibpas.pas [Truta] -- Update mkasm.bat in contrib/masmx86 [Truta] -- Update contrib/untgz [Truta] -- Add projects/README.projects [Truta] -- Add project for MS Visual C++ 6.0 in projects/visualc6 [Cadieux, Truta] -- Update win32/DLL_FAQ.txt [Truta] -- Update list of Z_PREFIX symbols in zconf.h [Randers-Pehrson, Truta] -- Remove an unnecessary assignment to curr in inftrees.c [Truta] -- Add OS/2 to exe builds in configure [Poltorak] -- Remove err dummy parameter in zlib.h [Kientzle] - -Changes in 1.2.1.1 (9 January 2004) -- Update email address in README -- Several FAQ updates -- Fix a big fat bug in inftrees.c that prevented decoding valid - dynamic blocks with only literals and no distance codes -- - Thanks to "Hot Emu" for the bug report and sample file -- Add a note to puff.c on no distance codes case. - -Changes in 1.2.1 (17 November 2003) -- Remove a tab in contrib/gzappend/gzappend.c -- Update some interfaces in contrib for new zlib functions -- Update zlib version number in some contrib entries -- Add Windows CE definition for ptrdiff_t in zutil.h [Mai, Truta] -- Support shared libraries on Hurd and KFreeBSD [Brown] -- Fix error in NO_DIVIDE option of adler32.c - -Changes in 1.2.0.8 (4 November 2003) -- Update version in contrib/delphi/ZLib.pas and contrib/pascal/zlibpas.pas -- Add experimental NO_DIVIDE #define in adler32.c - - Possibly faster on some processors (let me know if it is) -- Correct Z_BLOCK to not return on first inflate call if no wrap -- Fix strm->data_type on inflate() return to correctly indicate EOB -- Add deflatePrime() function for appending in the middle of a byte -- Add contrib/gzappend for an example of appending to a stream -- Update win32/DLL_FAQ.txt [Truta] -- Delete Turbo C comment in README [Truta] -- Improve some indentation in zconf.h [Truta] -- Fix infinite loop on bad input in configure script [Church] -- Fix gzeof() for concatenated gzip files [Johnson] -- Add example to contrib/visual-basic.txt [Michael B.] -- Add -p to mkdir's in Makefile.in [vda] -- Fix configure to properly detect presence or lack of printf functions -- Add AS400 support [Monnerat] -- Add a little Cygwin support [Wilson] - -Changes in 1.2.0.7 (21 September 2003) -- Correct some debug formats in contrib/infback9 -- Cast a type in a debug statement in trees.c -- Change search and replace delimiter in configure from % to # [Beebe] -- Update contrib/untgz to 0.2 with various fixes [Truta] -- Add build support for Amiga [Nikl] -- Remove some directories in old that have been updated to 1.2 -- Add dylib building for Mac OS X in configure and Makefile.in -- Remove old distribution stuff from Makefile -- Update README to point to DLL_FAQ.txt, and add comment on Mac OS X -- Update links in README - -Changes in 1.2.0.6 (13 September 2003) -- Minor FAQ updates -- Update contrib/minizip to 1.00 [Vollant] -- Remove test of gz functions in example.c when GZ_COMPRESS defined [Truta] -- Update POSTINC comment for 68060 [Nikl] -- Add contrib/infback9 with deflate64 decoding (unsupported) -- For MVS define NO_vsnprintf and undefine FAR [van Burik] -- Add pragma for fdopen on MVS [van Burik] - -Changes in 1.2.0.5 (8 September 2003) -- Add OF to inflateBackEnd() declaration in zlib.h -- Remember start when using gzdopen in the middle of a file -- Use internal off_t counters in gz* functions to properly handle seeks -- Perform more rigorous check for distance-too-far in inffast.c -- Add Z_BLOCK flush option to return from inflate at block boundary -- Set strm->data_type on return from inflate - - Indicate bits unused, if at block boundary, and if in last block -- Replace size_t with ptrdiff_t in crc32.c, and check for correct size -- Add condition so old NO_DEFLATE define still works for compatibility -- FAQ update regarding the Windows DLL [Truta] -- INDEX update: add qnx entry, remove aix entry [Truta] -- Install zlib.3 into mandir [Wilson] -- Move contrib/zlib_dll_FAQ.txt to win32/DLL_FAQ.txt; update [Truta] -- Adapt the zlib interface to the new DLL convention guidelines [Truta] -- Introduce ZLIB_WINAPI macro to allow the export of functions using - the WINAPI calling convention, for Visual Basic [Vollant, Truta] -- Update msdos and win32 scripts and makefiles [Truta] -- Export symbols by name, not by ordinal, in win32/zlib.def [Truta] -- Add contrib/ada [Anisimkov] -- Move asm files from contrib/vstudio/vc70_32 to contrib/asm386 [Truta] -- Rename contrib/asm386 to contrib/masmx86 [Truta, Vollant] -- Add contrib/masm686 [Truta] -- Fix offsets in contrib/inflate86 and contrib/masmx86/inffas32.asm - [Truta, Vollant] -- Update contrib/delphi; rename to contrib/pascal; add example [Truta] -- Remove contrib/delphi2; add a new contrib/delphi [Truta] -- Avoid inclusion of the nonstandard in contrib/iostream, - and fix some method prototypes [Truta] -- Fix the ZCR_SEED2 constant to avoid warnings in contrib/minizip - [Truta] -- Avoid the use of backslash (\) in contrib/minizip [Vollant] -- Fix file time handling in contrib/untgz; update makefiles [Truta] -- Update contrib/vstudio/vc70_32 to comply with the new DLL guidelines - [Vollant] -- Remove contrib/vstudio/vc15_16 [Vollant] -- Rename contrib/vstudio/vc70_32 to contrib/vstudio/vc7 [Truta] -- Update README.contrib [Truta] -- Invert the assignment order of match_head and s->prev[...] in - INSERT_STRING [Truta] -- Compare TOO_FAR with 32767 instead of 32768, to avoid 16-bit warnings - [Truta] -- Compare function pointers with 0, not with NULL or Z_NULL [Truta] -- Fix prototype of syncsearch in inflate.c [Truta] -- Introduce ASMINF macro to be enabled when using an ASM implementation - of inflate_fast [Truta] -- Change NO_DEFLATE to NO_GZCOMPRESS [Truta] -- Modify test_gzio in example.c to take a single file name as a - parameter [Truta] -- Exit the example.c program if gzopen fails [Truta] -- Add type casts around strlen in example.c [Truta] -- Remove casting to sizeof in minigzip.c; give a proper type - to the variable compared with SUFFIX_LEN [Truta] -- Update definitions of STDC and STDC99 in zconf.h [Truta] -- Synchronize zconf.h with the new Windows DLL interface [Truta] -- Use SYS16BIT instead of __32BIT__ to distinguish between - 16- and 32-bit platforms [Truta] -- Use far memory allocators in small 16-bit memory models for - Turbo C [Truta] -- Add info about the use of ASMV, ASMINF and ZLIB_WINAPI in - zlibCompileFlags [Truta] -- Cygwin has vsnprintf [Wilson] -- In Windows16, OS_CODE is 0, as in MSDOS [Truta] -- In Cygwin, OS_CODE is 3 (Unix), not 11 (Windows32) [Wilson] - -Changes in 1.2.0.4 (10 August 2003) -- Minor FAQ updates -- Be more strict when checking inflateInit2's windowBits parameter -- Change NO_GUNZIP compile option to NO_GZIP to cover deflate as well -- Add gzip wrapper option to deflateInit2 using windowBits -- Add updated QNX rule in configure and qnx directory [Bonnefoy] -- Make inflate distance-too-far checks more rigorous -- Clean up FAR usage in inflate -- Add casting to sizeof() in gzio.c and minigzip.c - -Changes in 1.2.0.3 (19 July 2003) -- Fix silly error in gzungetc() implementation [Vollant] -- Update contrib/minizip and contrib/vstudio [Vollant] -- Fix printf format in example.c -- Correct cdecl support in zconf.in.h [Anisimkov] -- Minor FAQ updates - -Changes in 1.2.0.2 (13 July 2003) -- Add ZLIB_VERNUM in zlib.h for numerical preprocessor comparisons -- Attempt to avoid warnings in crc32.c for pointer-int conversion -- Add AIX to configure, remove aix directory [Bakker] -- Add some casts to minigzip.c -- Improve checking after insecure sprintf() or vsprintf() calls -- Remove #elif's from crc32.c -- Change leave label to inf_leave in inflate.c and infback.c to avoid - library conflicts -- Remove inflate gzip decoding by default--only enable gzip decoding by - special request for stricter backward compatibility -- Add zlibCompileFlags() function to return compilation information -- More typecasting in deflate.c to avoid warnings -- Remove leading underscore from _Capital #defines [Truta] -- Fix configure to link shared library when testing -- Add some Windows CE target adjustments [Mai] -- Remove #define ZLIB_DLL in zconf.h [Vollant] -- Add zlib.3 [Rodgers] -- Update RFC URL in deflate.c and algorithm.txt [Mai] -- Add zlib_dll_FAQ.txt to contrib [Truta] -- Add UL to some constants [Truta] -- Update minizip and vstudio [Vollant] -- Remove vestigial NEED_DUMMY_RETURN from zconf.in.h -- Expand use of NO_DUMMY_DECL to avoid all dummy structures -- Added iostream3 to contrib [Schwardt] -- Replace rewind() with fseek() for WinCE [Truta] -- Improve setting of zlib format compression level flags - - Report 0 for huffman and rle strategies and for level == 0 or 1 - - Report 2 only for level == 6 -- Only deal with 64K limit when necessary at compile time [Truta] -- Allow TOO_FAR check to be turned off at compile time [Truta] -- Add gzclearerr() function [Souza] -- Add gzungetc() function - -Changes in 1.2.0.1 (17 March 2003) -- Add Z_RLE strategy for run-length encoding [Truta] - - When Z_RLE requested, restrict matches to distance one - - Update zlib.h, minigzip.c, gzopen(), gzdopen() for Z_RLE -- Correct FASTEST compilation to allow level == 0 -- Clean up what gets compiled for FASTEST -- Incorporate changes to zconf.in.h [Vollant] - - Refine detection of Turbo C need for dummy returns - - Refine ZLIB_DLL compilation - - Include additional header file on VMS for off_t typedef -- Try to use _vsnprintf where it supplants vsprintf [Vollant] -- Add some casts in inffast.c -- Enchance comments in zlib.h on what happens if gzprintf() tries to - write more than 4095 bytes before compression -- Remove unused state from inflateBackEnd() -- Remove exit(0) from minigzip.c, example.c -- Get rid of all those darn tabs -- Add "check" target to Makefile.in that does the same thing as "test" -- Add "mostlyclean" and "maintainer-clean" targets to Makefile.in -- Update contrib/inflate86 [Anderson] -- Update contrib/testzlib, contrib/vstudio, contrib/minizip [Vollant] -- Add msdos and win32 directories with makefiles [Truta] -- More additions and improvements to the FAQ - -Changes in 1.2.0 (9 March 2003) -- New and improved inflate code - - About 20% faster - - Does not allocate 32K window unless and until needed - - Automatically detects and decompresses gzip streams - - Raw inflate no longer needs an extra dummy byte at end - - Added inflateBack functions using a callback interface--even faster - than inflate, useful for file utilities (gzip, zip) - - Added inflateCopy() function to record state for random access on - externally generated deflate streams (e.g. in gzip files) - - More readable code (I hope) -- New and improved crc32() - - About 50% faster, thanks to suggestions from Rodney Brown -- Add deflateBound() and compressBound() functions -- Fix memory leak in deflateInit2() -- Permit setting dictionary for raw deflate (for parallel deflate) -- Fix const declaration for gzwrite() -- Check for some malloc() failures in gzio.c -- Fix bug in gzopen() on single-byte file 0x1f -- Fix bug in gzread() on concatenated file with 0x1f at end of buffer - and next buffer doesn't start with 0x8b -- Fix uncompress() to return Z_DATA_ERROR on truncated input -- Free memory at end of example.c -- Remove MAX #define in trees.c (conflicted with some libraries) -- Fix static const's in deflate.c, gzio.c, and zutil.[ch] -- Declare malloc() and free() in gzio.c if STDC not defined -- Use malloc() instead of calloc() in zutil.c if int big enough -- Define STDC for AIX -- Add aix/ with approach for compiling shared library on AIX -- Add HP-UX support for shared libraries in configure -- Add OpenUNIX support for shared libraries in configure -- Use $cc instead of gcc to build shared library -- Make prefix directory if needed when installing -- Correct Macintosh avoidance of typedef Byte in zconf.h -- Correct Turbo C memory allocation when under Linux -- Use libz.a instead of -lz in Makefile (assure use of compiled library) -- Update configure to check for snprintf or vsnprintf functions and their - return value, warn during make if using an insecure function -- Fix configure problem with compile-time knowledge of HAVE_UNISTD_H that - is lost when library is used--resolution is to build new zconf.h -- Documentation improvements (in zlib.h): - - Document raw deflate and inflate - - Update RFCs URL - - Point out that zlib and gzip formats are different - - Note that Z_BUF_ERROR is not fatal - - Document string limit for gzprintf() and possible buffer overflow - - Note requirement on avail_out when flushing - - Note permitted values of flush parameter of inflate() -- Add some FAQs (and even answers) to the FAQ -- Add contrib/inflate86/ for x86 faster inflate -- Add contrib/blast/ for PKWare Data Compression Library decompression -- Add contrib/puff/ simple inflate for deflate format description - -Changes in 1.1.4 (11 March 2002) -- ZFREE was repeated on same allocation on some error conditions. - This creates a security problem described in - http://www.zlib.org/advisory-2002-03-11.txt -- Returned incorrect error (Z_MEM_ERROR) on some invalid data -- Avoid accesses before window for invalid distances with inflate window - less than 32K. -- force windowBits > 8 to avoid a bug in the encoder for a window size - of 256 bytes. (A complete fix will be available in 1.1.5). - -Changes in 1.1.3 (9 July 1998) -- fix "an inflate input buffer bug that shows up on rare but persistent - occasions" (Mark) -- fix gzread and gztell for concatenated .gz files (Didier Le Botlan) -- fix gzseek(..., SEEK_SET) in write mode -- fix crc check after a gzeek (Frank Faubert) -- fix miniunzip when the last entry in a zip file is itself a zip file - (J Lillge) -- add contrib/asm586 and contrib/asm686 (Brian Raiter) - See http://www.muppetlabs.com/~breadbox/software/assembly.html -- add support for Delphi 3 in contrib/delphi (Bob Dellaca) -- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti) -- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren) -- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks) -- added a FAQ file - -- Support gzdopen on Mac with Metrowerks (Jason Linhart) -- Do not redefine Byte on Mac (Brad Pettit & Jason Linhart) -- define SEEK_END too if SEEK_SET is not defined (Albert Chin-A-Young) -- avoid some warnings with Borland C (Tom Tanner) -- fix a problem in contrib/minizip/zip.c for 16-bit MSDOS (Gilles Vollant) -- emulate utime() for WIN32 in contrib/untgz (Gilles Vollant) -- allow several arguments to configure (Tim Mooney, Frodo Looijaard) -- use libdir and includedir in Makefile.in (Tim Mooney) -- support shared libraries on OSF1 V4 (Tim Mooney) -- remove so_locations in "make clean" (Tim Mooney) -- fix maketree.c compilation error (Glenn, Mark) -- Python interface to zlib now in Python 1.5 (Jeremy Hylton) -- new Makefile.riscos (Rich Walker) -- initialize static descriptors in trees.c for embedded targets (Nick Smith) -- use "foo-gz" in example.c for RISCOS and VMS (Nick Smith) -- add the OS/2 files in Makefile.in too (Andrew Zabolotny) -- fix fdopen and halloc macros for Microsoft C 6.0 (Tom Lane) -- fix maketree.c to allow clean compilation of inffixed.h (Mark) -- fix parameter check in deflateCopy (Gunther Nikl) -- cleanup trees.c, use compressed_len only in debug mode (Christian Spieler) -- Many portability patches by Christian Spieler: - . zutil.c, zutil.h: added "const" for zmem* - . Make_vms.com: fixed some typos - . Make_vms.com: msdos/Makefile.*: removed zutil.h from some dependency lists - . msdos/Makefile.msc: remove "default rtl link library" info from obj files - . msdos/Makefile.*: use model-dependent name for the built zlib library - . msdos/Makefile.emx, nt/Makefile.emx, nt/Makefile.gcc: - new makefiles, for emx (DOS/OS2), emx&rsxnt and mingw32 (Windows 9x / NT) -- use define instead of typedef for Bytef also for MSC small/medium (Tom Lane) -- replace __far with _far for better portability (Christian Spieler, Tom Lane) -- fix test for errno.h in configure (Tim Newsham) - -Changes in 1.1.2 (19 March 98) -- added contrib/minzip, mini zip and unzip based on zlib (Gilles Vollant) - See http://www.winimage.com/zLibDll/unzip.html -- preinitialize the inflate tables for fixed codes, to make the code - completely thread safe (Mark) -- some simplifications and slight speed-up to the inflate code (Mark) -- fix gzeof on non-compressed files (Allan Schrum) -- add -std1 option in configure for OSF1 to fix gzprintf (Martin Mokrejs) -- use default value of 4K for Z_BUFSIZE for 16-bit MSDOS (Tim Wegner + Glenn) -- added os2/Makefile.def and os2/zlib.def (Andrew Zabolotny) -- add shared lib support for UNIX_SV4.2MP (MATSUURA Takanori) -- do not wrap extern "C" around system includes (Tom Lane) -- mention zlib binding for TCL in README (Andreas Kupries) -- added amiga/Makefile.pup for Amiga powerUP SAS/C PPC (Andreas Kleinert) -- allow "make install prefix=..." even after configure (Glenn Randers-Pehrson) -- allow "configure --prefix $HOME" (Tim Mooney) -- remove warnings in example.c and gzio.c (Glenn Randers-Pehrson) -- move Makefile.sas to amiga/Makefile.sas - -Changes in 1.1.1 (27 Feb 98) -- fix macros _tr_tally_* in deflate.h for debug mode (Glenn Randers-Pehrson) -- remove block truncation heuristic which had very marginal effect for zlib - (smaller lit_bufsize than in gzip 1.2.4) and degraded a little the - compression ratio on some files. This also allows inlining _tr_tally for - matches in deflate_slow. -- added msdos/Makefile.w32 for WIN32 Microsoft Visual C++ (Bob Frazier) - -Changes in 1.1.0 (24 Feb 98) -- do not return STREAM_END prematurely in inflate (John Bowler) -- revert to the zlib 1.0.8 inflate to avoid the gcc 2.8.0 bug (Jeremy Buhler) -- compile with -DFASTEST to get compression code optimized for speed only -- in minigzip, try mmap'ing the input file first (Miguel Albrecht) -- increase size of I/O buffers in minigzip.c and gzio.c (not a big gain - on Sun but significant on HP) - -- add a pointer to experimental unzip library in README (Gilles Vollant) -- initialize variable gcc in configure (Chris Herborth) - -Changes in 1.0.9 (17 Feb 1998) -- added gzputs and gzgets functions -- do not clear eof flag in gzseek (Mark Diekhans) -- fix gzseek for files in transparent mode (Mark Diekhans) -- do not assume that vsprintf returns the number of bytes written (Jens Krinke) -- replace EXPORT with ZEXPORT to avoid conflict with other programs -- added compress2 in zconf.h, zlib.def, zlib.dnt -- new asm code from Gilles Vollant in contrib/asm386 -- simplify the inflate code (Mark): - . Replace ZALLOC's in huft_build() with single ZALLOC in inflate_blocks_new() - . ZALLOC the length list in inflate_trees_fixed() instead of using stack - . ZALLOC the value area for huft_build() instead of using stack - . Simplify Z_FINISH check in inflate() - -- Avoid gcc 2.8.0 comparison bug a little differently than zlib 1.0.8 -- in inftrees.c, avoid cc -O bug on HP (Farshid Elahi) -- in zconf.h move the ZLIB_DLL stuff earlier to avoid problems with - the declaration of FAR (Gilles VOllant) -- install libz.so* with mode 755 (executable) instead of 644 (Marc Lehmann) -- read_buf buf parameter of type Bytef* instead of charf* -- zmemcpy parameters are of type Bytef*, not charf* (Joseph Strout) -- do not redeclare unlink in minigzip.c for WIN32 (John Bowler) -- fix check for presence of directories in "make install" (Ian Willis) - -Changes in 1.0.8 (27 Jan 1998) -- fixed offsets in contrib/asm386/gvmat32.asm (Gilles Vollant) -- fix gzgetc and gzputc for big endian systems (Markus Oberhumer) -- added compress2() to allow setting the compression level -- include sys/types.h to get off_t on some systems (Marc Lehmann & QingLong) -- use constant arrays for the static trees in trees.c instead of computing - them at run time (thanks to Ken Raeburn for this suggestion). To create - trees.h, compile with GEN_TREES_H and run "make test". -- check return code of example in "make test" and display result -- pass minigzip command line options to file_compress -- simplifying code of inflateSync to avoid gcc 2.8 bug - -- support CC="gcc -Wall" in configure -s (QingLong) -- avoid a flush caused by ftell in gzopen for write mode (Ken Raeburn) -- fix test for shared library support to avoid compiler warnings -- zlib.lib -> zlib.dll in msdos/zlib.rc (Gilles Vollant) -- check for TARGET_OS_MAC in addition to MACOS (Brad Pettit) -- do not use fdopen for Metrowerks on Mac (Brad Pettit)) -- add checks for gzputc and gzputc in example.c -- avoid warnings in gzio.c and deflate.c (Andreas Kleinert) -- use const for the CRC table (Ken Raeburn) -- fixed "make uninstall" for shared libraries -- use Tracev instead of Trace in infblock.c -- in example.c use correct compressed length for test_sync -- suppress +vnocompatwarnings in configure for HPUX (not always supported) - -Changes in 1.0.7 (20 Jan 1998) -- fix gzseek which was broken in write mode -- return error for gzseek to negative absolute position -- fix configure for Linux (Chun-Chung Chen) -- increase stack space for MSC (Tim Wegner) -- get_crc_table and inflateSyncPoint are EXPORTed (Gilles Vollant) -- define EXPORTVA for gzprintf (Gilles Vollant) -- added man page zlib.3 (Rick Rodgers) -- for contrib/untgz, fix makedir() and improve Makefile - -- check gzseek in write mode in example.c -- allocate extra buffer for seeks only if gzseek is actually called -- avoid signed/unsigned comparisons (Tim Wegner, Gilles Vollant) -- add inflateSyncPoint in zconf.h -- fix list of exported functions in nt/zlib.dnt and mdsos/zlib.def - -Changes in 1.0.6 (19 Jan 1998) -- add functions gzprintf, gzputc, gzgetc, gztell, gzeof, gzseek, gzrewind and - gzsetparams (thanks to Roland Giersig and Kevin Ruland for some of this code) -- Fix a deflate bug occurring only with compression level 0 (thanks to - Andy Buckler for finding this one). -- In minigzip, pass transparently also the first byte for .Z files. -- return Z_BUF_ERROR instead of Z_OK if output buffer full in uncompress() -- check Z_FINISH in inflate (thanks to Marc Schluper) -- Implement deflateCopy (thanks to Adam Costello) -- make static libraries by default in configure, add --shared option. -- move MSDOS or Windows specific files to directory msdos -- suppress the notion of partial flush to simplify the interface - (but the symbol Z_PARTIAL_FLUSH is kept for compatibility with 1.0.4) -- suppress history buffer provided by application to simplify the interface - (this feature was not implemented anyway in 1.0.4) -- next_in and avail_in must be initialized before calling inflateInit or - inflateInit2 -- add EXPORT in all exported functions (for Windows DLL) -- added Makefile.nt (thanks to Stephen Williams) -- added the unsupported "contrib" directory: - contrib/asm386/ by Gilles Vollant - 386 asm code replacing longest_match(). - contrib/iostream/ by Kevin Ruland - A C++ I/O streams interface to the zlib gz* functions - contrib/iostream2/ by Tyge Løvset - Another C++ I/O streams interface - contrib/untgz/ by "Pedro A. Aranda Guti\irrez" - A very simple tar.gz file extractor using zlib - contrib/visual-basic.txt by Carlos Rios - How to use compress(), uncompress() and the gz* functions from VB. -- pass params -f (filtered data), -h (huffman only), -1 to -9 (compression - level) in minigzip (thanks to Tom Lane) - -- use const for rommable constants in deflate -- added test for gzseek and gztell in example.c -- add undocumented function inflateSyncPoint() (hack for Paul Mackerras) -- add undocumented function zError to convert error code to string - (for Tim Smithers) -- Allow compilation of gzio with -DNO_DEFLATE to avoid the compression code. -- Use default memcpy for Symantec MSDOS compiler. -- Add EXPORT keyword for check_func (needed for Windows DLL) -- add current directory to LD_LIBRARY_PATH for "make test" -- create also a link for libz.so.1 -- added support for FUJITSU UXP/DS (thanks to Toshiaki Nomura) -- use $(SHAREDLIB) instead of libz.so in Makefile.in (for HPUX) -- added -soname for Linux in configure (Chun-Chung Chen, -- assign numbers to the exported functions in zlib.def (for Windows DLL) -- add advice in zlib.h for best usage of deflateSetDictionary -- work around compiler bug on Atari (cast Z_NULL in call of s->checkfn) -- allow compilation with ANSI keywords only enabled for TurboC in large model -- avoid "versionString"[0] (Borland bug) -- add NEED_DUMMY_RETURN for Borland -- use variable z_verbose for tracing in debug mode (L. Peter Deutsch). -- allow compilation with CC -- defined STDC for OS/2 (David Charlap) -- limit external names to 8 chars for MVS (Thomas Lund) -- in minigzip.c, use static buffers only for 16-bit systems -- fix suffix check for "minigzip -d foo.gz" -- do not return an error for the 2nd of two consecutive gzflush() (Felix Lee) -- use _fdopen instead of fdopen for MSC >= 6.0 (Thomas Fanslau) -- added makelcc.bat for lcc-win32 (Tom St Denis) -- in Makefile.dj2, use copy and del instead of install and rm (Frank Donahoe) -- Avoid expanded $Id$. Use "rcs -kb" or "cvs admin -kb" to avoid Id expansion. -- check for unistd.h in configure (for off_t) -- remove useless check parameter in inflate_blocks_free -- avoid useless assignment of s->check to itself in inflate_blocks_new -- do not flush twice in gzclose (thanks to Ken Raeburn) -- rename FOPEN as F_OPEN to avoid clash with /usr/include/sys/file.h -- use NO_ERRNO_H instead of enumeration of operating systems with errno.h -- work around buggy fclose on pipes for HP/UX -- support zlib DLL with BORLAND C++ 5.0 (thanks to Glenn Randers-Pehrson) -- fix configure if CC is already equal to gcc - -Changes in 1.0.5 (3 Jan 98) -- Fix inflate to terminate gracefully when fed corrupted or invalid data -- Use const for rommable constants in inflate -- Eliminate memory leaks on error conditions in inflate -- Removed some vestigial code in inflate -- Update web address in README - -Changes in 1.0.4 (24 Jul 96) -- In very rare conditions, deflate(s, Z_FINISH) could fail to produce an EOF - bit, so the decompressor could decompress all the correct data but went - on to attempt decompressing extra garbage data. This affected minigzip too. -- zlibVersion and gzerror return const char* (needed for DLL) -- port to RISCOS (no fdopen, no multiple dots, no unlink, no fileno) -- use z_error only for DEBUG (avoid problem with DLLs) - -Changes in 1.0.3 (2 Jul 96) -- use z_streamp instead of z_stream *, which is now a far pointer in MSDOS - small and medium models; this makes the library incompatible with previous - versions for these models. (No effect in large model or on other systems.) -- return OK instead of BUF_ERROR if previous deflate call returned with - avail_out as zero but there is nothing to do -- added memcmp for non STDC compilers -- define NO_DUMMY_DECL for more Mac compilers (.h files merged incorrectly) -- define __32BIT__ if __386__ or i386 is defined (pb. with Watcom and SCO) -- better check for 16-bit mode MSC (avoids problem with Symantec) - -Changes in 1.0.2 (23 May 96) -- added Windows DLL support -- added a function zlibVersion (for the DLL support) -- fixed declarations using Bytef in infutil.c (pb with MSDOS medium model) -- Bytef is define's instead of typedef'd only for Borland C -- avoid reading uninitialized memory in example.c -- mention in README that the zlib format is now RFC1950 -- updated Makefile.dj2 -- added algorithm.doc - -Changes in 1.0.1 (20 May 96) [1.0 skipped to avoid confusion] -- fix array overlay in deflate.c which sometimes caused bad compressed data -- fix inflate bug with empty stored block -- fix MSDOS medium model which was broken in 0.99 -- fix deflateParams() which could generate bad compressed data. -- Bytef is define'd instead of typedef'ed (work around Borland bug) -- added an INDEX file -- new makefiles for DJGPP (Makefile.dj2), 32-bit Borland (Makefile.b32), - Watcom (Makefile.wat), Amiga SAS/C (Makefile.sas) -- speed up adler32 for modern machines without auto-increment -- added -ansi for IRIX in configure -- static_init_done in trees.c is an int -- define unlink as delete for VMS -- fix configure for QNX -- add configure branch for SCO and HPUX -- avoid many warnings (unused variables, dead assignments, etc...) -- no fdopen for BeOS -- fix the Watcom fix for 32 bit mode (define FAR as empty) -- removed redefinition of Byte for MKWERKS -- work around an MWKERKS bug (incorrect merge of all .h files) - -Changes in 0.99 (27 Jan 96) -- allow preset dictionary shared between compressor and decompressor -- allow compression level 0 (no compression) -- add deflateParams in zlib.h: allow dynamic change of compression level - and compression strategy. -- test large buffers and deflateParams in example.c -- add optional "configure" to build zlib as a shared library -- suppress Makefile.qnx, use configure instead -- fixed deflate for 64-bit systems (detected on Cray) -- fixed inflate_blocks for 64-bit systems (detected on Alpha) -- declare Z_DEFLATED in zlib.h (possible parameter for deflateInit2) -- always return Z_BUF_ERROR when deflate() has nothing to do -- deflateInit and inflateInit are now macros to allow version checking -- prefix all global functions and types with z_ with -DZ_PREFIX -- make falloc completely reentrant (inftrees.c) -- fixed very unlikely race condition in ct_static_init -- free in reverse order of allocation to help memory manager -- use zlib-1.0/* instead of zlib/* inside the tar.gz -- make zlib warning-free with "gcc -O3 -Wall -Wwrite-strings -Wpointer-arith - -Wconversion -Wstrict-prototypes -Wmissing-prototypes" -- allow gzread on concatenated .gz files -- deflateEnd now returns Z_DATA_ERROR if it was premature -- deflate is finally (?) fully deterministic (no matches beyond end of input) -- Document Z_SYNC_FLUSH -- add uninstall in Makefile -- Check for __cpluplus in zlib.h -- Better test in ct_align for partial flush -- avoid harmless warnings for Borland C++ -- initialize hash_head in deflate.c -- avoid warning on fdopen (gzio.c) for HP cc -Aa -- include stdlib.h for STDC compilers -- include errno.h for Cray -- ignore error if ranlib doesn't exist -- call ranlib twice for NeXTSTEP -- use exec_prefix instead of prefix for libz.a -- renamed ct_* as _tr_* to avoid conflict with applications -- clear z->msg in inflateInit2 before any error return -- initialize opaque in example.c, gzio.c, deflate.c and inflate.c -- fixed typo in zconf.h (_GNUC__ => __GNUC__) -- check for WIN32 in zconf.h and zutil.c (avoid farmalloc in 32-bit mode) -- fix typo in Make_vms.com (f$trnlnm -> f$getsyi) -- in fcalloc, normalize pointer if size > 65520 bytes -- don't use special fcalloc for 32 bit Borland C++ -- use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc... -- use Z_BINARY instead of BINARY -- document that gzclose after gzdopen will close the file -- allow "a" as mode in gzopen. -- fix error checking in gzread -- allow skipping .gz extra-field on pipes -- added reference to Perl interface in README -- put the crc table in FAR data (I dislike more and more the medium model :) -- added get_crc_table -- added a dimension to all arrays (Borland C can't count). -- workaround Borland C bug in declaration of inflate_codes_new & inflate_fast -- guard against multiple inclusion of *.h (for precompiled header on Mac) -- Watcom C pretends to be Microsoft C small model even in 32 bit mode. -- don't use unsized arrays to avoid silly warnings by Visual C++: - warning C4746: 'inflate_mask' : unsized array treated as '__far' - (what's wrong with far data in far model?). -- define enum out of inflate_blocks_state to allow compilation with C++ - -Changes in 0.95 (16 Aug 95) -- fix MSDOS small and medium model (now easier to adapt to any compiler) -- inlined send_bits -- fix the final (:-) bug for deflate with flush (output was correct but - not completely flushed in rare occasions). -- default window size is same for compression and decompression - (it's now sufficient to set MAX_WBITS in zconf.h). -- voidp -> voidpf and voidnp -> voidp (for consistency with other - typedefs and because voidnp was not near in large model). - -Changes in 0.94 (13 Aug 95) -- support MSDOS medium model -- fix deflate with flush (could sometimes generate bad output) -- fix deflateReset (zlib header was incorrectly suppressed) -- added support for VMS -- allow a compression level in gzopen() -- gzflush now calls fflush -- For deflate with flush, flush even if no more input is provided. -- rename libgz.a as libz.a -- avoid complex expression in infcodes.c triggering Turbo C bug -- work around a problem with gcc on Alpha (in INSERT_STRING) -- don't use inline functions (problem with some gcc versions) -- allow renaming of Byte, uInt, etc... with #define. -- avoid warning about (unused) pointer before start of array in deflate.c -- avoid various warnings in gzio.c, example.c, infblock.c, adler32.c, zutil.c -- avoid reserved word 'new' in trees.c - -Changes in 0.93 (25 June 95) -- temporarily disable inline functions -- make deflate deterministic -- give enough lookahead for PARTIAL_FLUSH -- Set binary mode for stdin/stdout in minigzip.c for OS/2 -- don't even use signed char in inflate (not portable enough) -- fix inflate memory leak for segmented architectures - -Changes in 0.92 (3 May 95) -- don't assume that char is signed (problem on SGI) -- Clear bit buffer when starting a stored block -- no memcpy on Pyramid -- suppressed inftest.c -- optimized fill_window, put longest_match inline for gcc -- optimized inflate on stored blocks. -- untabify all sources to simplify patches - -Changes in 0.91 (2 May 95) -- Default MEM_LEVEL is 8 (not 9 for Unix) as documented in zlib.h -- Document the memory requirements in zconf.h -- added "make install" -- fix sync search logic in inflateSync -- deflate(Z_FULL_FLUSH) now works even if output buffer too short -- after inflateSync, don't scare people with just "lo world" -- added support for DJGPP - -Changes in 0.9 (1 May 95) -- don't assume that zalloc clears the allocated memory (the TurboC bug - was Mark's bug after all :) -- let again gzread copy uncompressed data unchanged (was working in 0.71) -- deflate(Z_FULL_FLUSH), inflateReset and inflateSync are now fully implemented -- added a test of inflateSync in example.c -- moved MAX_WBITS to zconf.h because users might want to change that. -- document explicitly that zalloc(64K) on MSDOS must return a normalized - pointer (zero offset) -- added Makefiles for Microsoft C, Turbo C, Borland C++ -- faster crc32() - -Changes in 0.8 (29 April 95) -- added fast inflate (inffast.c) -- deflate(Z_FINISH) now returns Z_STREAM_END when done. Warning: this - is incompatible with previous versions of zlib which returned Z_OK. -- work around a TurboC compiler bug (bad code for b << 0, see infutil.h) - (actually that was not a compiler bug, see 0.81 above) -- gzread no longer reads one extra byte in certain cases -- In gzio destroy(), don't reference a freed structure -- avoid many warnings for MSDOS -- avoid the ERROR symbol which is used by MS Windows - -Changes in 0.71 (14 April 95) -- Fixed more MSDOS compilation problems :( There is still a bug with - TurboC large model. - -Changes in 0.7 (14 April 95) -- Added full inflate support. -- Simplified the crc32() interface. The pre- and post-conditioning - (one's complement) is now done inside crc32(). WARNING: this is - incompatible with previous versions; see zlib.h for the new usage. - -Changes in 0.61 (12 April 95) -- workaround for a bug in TurboC. example and minigzip now work on MSDOS. - -Changes in 0.6 (11 April 95) -- added minigzip.c -- added gzdopen to reopen a file descriptor as gzFile -- added transparent reading of non-gziped files in gzread. -- fixed bug in gzread (don't read crc as data) -- fixed bug in destroy (gzio.c) (don't return Z_STREAM_END for gzclose). -- don't allocate big arrays in the stack (for MSDOS) -- fix some MSDOS compilation problems - -Changes in 0.5: -- do real compression in deflate.c. Z_PARTIAL_FLUSH is supported but - not yet Z_FULL_FLUSH. -- support decompression but only in a single step (forced Z_FINISH) -- added opaque object for zalloc and zfree. -- added deflateReset and inflateReset -- added a variable zlib_version for consistency checking. -- renamed the 'filter' parameter of deflateInit2 as 'strategy'. - Added Z_FILTERED and Z_HUFFMAN_ONLY constants. - -Changes in 0.4: -- avoid "zip" everywhere, use zlib instead of ziplib. -- suppress Z_BLOCK_FLUSH, interpret Z_PARTIAL_FLUSH as block flush - if compression method == 8. -- added adler32 and crc32 -- renamed deflateOptions as deflateInit2, call one or the other but not both -- added the method parameter for deflateInit2. -- added inflateInit2 -- simplied considerably deflateInit and inflateInit by not supporting - user-provided history buffer. This is supported only in deflateInit2 - and inflateInit2. - -Changes in 0.3: -- prefix all macro names with Z_ -- use Z_FINISH instead of deflateEnd to finish compression. -- added Z_HUFFMAN_ONLY -- added gzerror() diff --git a/contrib/zlib/FAQ b/contrib/zlib/FAQ deleted file mode 100644 index 99b7cf92e..000000000 --- a/contrib/zlib/FAQ +++ /dev/null @@ -1,368 +0,0 @@ - - Frequently Asked Questions about zlib - - -If your question is not there, please check the zlib home page -http://zlib.net/ which may have more recent information. -The lastest zlib FAQ is at http://zlib.net/zlib_faq.html - - - 1. Is zlib Y2K-compliant? - - Yes. zlib doesn't handle dates. - - 2. Where can I get a Windows DLL version? - - The zlib sources can be compiled without change to produce a DLL. See the - file win32/DLL_FAQ.txt in the zlib distribution. Pointers to the - precompiled DLL are found in the zlib web site at http://zlib.net/ . - - 3. Where can I get a Visual Basic interface to zlib? - - See - * http://marknelson.us/1997/01/01/zlib-engine/ - * win32/DLL_FAQ.txt in the zlib distribution - - 4. compress() returns Z_BUF_ERROR. - - Make sure that before the call of compress(), the length of the compressed - buffer is equal to the available size of the compressed buffer and not - zero. For Visual Basic, check that this parameter is passed by reference - ("as any"), not by value ("as long"). - - 5. deflate() or inflate() returns Z_BUF_ERROR. - - Before making the call, make sure that avail_in and avail_out are not zero. - When setting the parameter flush equal to Z_FINISH, also make sure that - avail_out is big enough to allow processing all pending input. Note that a - Z_BUF_ERROR is not fatal--another call to deflate() or inflate() can be - made with more input or output space. A Z_BUF_ERROR may in fact be - unavoidable depending on how the functions are used, since it is not - possible to tell whether or not there is more output pending when - strm.avail_out returns with zero. See http://zlib.net/zlib_how.html for a - heavily annotated example. - - 6. Where's the zlib documentation (man pages, etc.)? - - It's in zlib.h . Examples of zlib usage are in the files test/example.c - and test/minigzip.c, with more in examples/ . - - 7. Why don't you use GNU autoconf or libtool or ...? - - Because we would like to keep zlib as a very small and simple package. - zlib is rather portable and doesn't need much configuration. - - 8. I found a bug in zlib. - - Most of the time, such problems are due to an incorrect usage of zlib. - Please try to reproduce the problem with a small program and send the - corresponding source to us at zlib@gzip.org . Do not send multi-megabyte - data files without prior agreement. - - 9. Why do I get "undefined reference to gzputc"? - - If "make test" produces something like - - example.o(.text+0x154): undefined reference to `gzputc' - - check that you don't have old files libz.* in /usr/lib, /usr/local/lib or - /usr/X11R6/lib. Remove any old versions, then do "make install". - -10. I need a Delphi interface to zlib. - - See the contrib/delphi directory in the zlib distribution. - -11. Can zlib handle .zip archives? - - Not by itself, no. See the directory contrib/minizip in the zlib - distribution. - -12. Can zlib handle .Z files? - - No, sorry. You have to spawn an uncompress or gunzip subprocess, or adapt - the code of uncompress on your own. - -13. How can I make a Unix shared library? - - By default a shared (and a static) library is built for Unix. So: - - make distclean - ./configure - make - -14. How do I install a shared zlib library on Unix? - - After the above, then: - - make install - - However, many flavors of Unix come with a shared zlib already installed. - Before going to the trouble of compiling a shared version of zlib and - trying to install it, you may want to check if it's already there! If you - can #include , it's there. The -lz option will probably link to - it. You can check the version at the top of zlib.h or with the - ZLIB_VERSION symbol defined in zlib.h . - -15. I have a question about OttoPDF. - - We are not the authors of OttoPDF. The real author is on the OttoPDF web - site: Joel Hainley, jhainley@myndkryme.com. - -16. Can zlib decode Flate data in an Adobe PDF file? - - Yes. See http://www.pdflib.com/ . To modify PDF forms, see - http://sourceforge.net/projects/acroformtool/ . - -17. Why am I getting this "register_frame_info not found" error on Solaris? - - After installing zlib 1.1.4 on Solaris 2.6, running applications using zlib - generates an error such as: - - ld.so.1: rpm: fatal: relocation error: file /usr/local/lib/libz.so: - symbol __register_frame_info: referenced symbol not found - - The symbol __register_frame_info is not part of zlib, it is generated by - the C compiler (cc or gcc). You must recompile applications using zlib - which have this problem. This problem is specific to Solaris. See - http://www.sunfreeware.com for Solaris versions of zlib and applications - using zlib. - -18. Why does gzip give an error on a file I make with compress/deflate? - - The compress and deflate functions produce data in the zlib format, which - is different and incompatible with the gzip format. The gz* functions in - zlib on the other hand use the gzip format. Both the zlib and gzip formats - use the same compressed data format internally, but have different headers - and trailers around the compressed data. - -19. Ok, so why are there two different formats? - - The gzip format was designed to retain the directory information about a - single file, such as the name and last modification date. The zlib format - on the other hand was designed for in-memory and communication channel - applications, and has a much more compact header and trailer and uses a - faster integrity check than gzip. - -20. Well that's nice, but how do I make a gzip file in memory? - - You can request that deflate write the gzip format instead of the zlib - format using deflateInit2(). You can also request that inflate decode the - gzip format using inflateInit2(). Read zlib.h for more details. - -21. Is zlib thread-safe? - - Yes. However any library routines that zlib uses and any application- - provided memory allocation routines must also be thread-safe. zlib's gz* - functions use stdio library routines, and most of zlib's functions use the - library memory allocation routines by default. zlib's *Init* functions - allow for the application to provide custom memory allocation routines. - - Of course, you should only operate on any given zlib or gzip stream from a - single thread at a time. - -22. Can I use zlib in my commercial application? - - Yes. Please read the license in zlib.h. - -23. Is zlib under the GNU license? - - No. Please read the license in zlib.h. - -24. The license says that altered source versions must be "plainly marked". So - what exactly do I need to do to meet that requirement? - - You need to change the ZLIB_VERSION and ZLIB_VERNUM #defines in zlib.h. In - particular, the final version number needs to be changed to "f", and an - identification string should be appended to ZLIB_VERSION. Version numbers - x.x.x.f are reserved for modifications to zlib by others than the zlib - maintainers. For example, if the version of the base zlib you are altering - is "1.2.3.4", then in zlib.h you should change ZLIB_VERNUM to 0x123f, and - ZLIB_VERSION to something like "1.2.3.f-zachary-mods-v3". You can also - update the version strings in deflate.c and inftrees.c. - - For altered source distributions, you should also note the origin and - nature of the changes in zlib.h, as well as in ChangeLog and README, along - with the dates of the alterations. The origin should include at least your - name (or your company's name), and an email address to contact for help or - issues with the library. - - Note that distributing a compiled zlib library along with zlib.h and - zconf.h is also a source distribution, and so you should change - ZLIB_VERSION and ZLIB_VERNUM and note the origin and nature of the changes - in zlib.h as you would for a full source distribution. - -25. Will zlib work on a big-endian or little-endian architecture, and can I - exchange compressed data between them? - - Yes and yes. - -26. Will zlib work on a 64-bit machine? - - Yes. It has been tested on 64-bit machines, and has no dependence on any - data types being limited to 32-bits in length. If you have any - difficulties, please provide a complete problem report to zlib@gzip.org - -27. Will zlib decompress data from the PKWare Data Compression Library? - - No. The PKWare DCL uses a completely different compressed data format than - does PKZIP and zlib. However, you can look in zlib's contrib/blast - directory for a possible solution to your problem. - -28. Can I access data randomly in a compressed stream? - - No, not without some preparation. If when compressing you periodically use - Z_FULL_FLUSH, carefully write all the pending data at those points, and - keep an index of those locations, then you can start decompression at those - points. You have to be careful to not use Z_FULL_FLUSH too often, since it - can significantly degrade compression. Alternatively, you can scan a - deflate stream once to generate an index, and then use that index for - random access. See examples/zran.c . - -29. Does zlib work on MVS, OS/390, CICS, etc.? - - It has in the past, but we have not heard of any recent evidence. There - were working ports of zlib 1.1.4 to MVS, but those links no longer work. - If you know of recent, successful applications of zlib on these operating - systems, please let us know. Thanks. - -30. Is there some simpler, easier to read version of inflate I can look at to - understand the deflate format? - - First off, you should read RFC 1951. Second, yes. Look in zlib's - contrib/puff directory. - -31. Does zlib infringe on any patents? - - As far as we know, no. In fact, that was originally the whole point behind - zlib. Look here for some more information: - - http://www.gzip.org/#faq11 - -32. Can zlib work with greater than 4 GB of data? - - Yes. inflate() and deflate() will process any amount of data correctly. - Each call of inflate() or deflate() is limited to input and output chunks - of the maximum value that can be stored in the compiler's "unsigned int" - type, but there is no limit to the number of chunks. Note however that the - strm.total_in and strm_total_out counters may be limited to 4 GB. These - counters are provided as a convenience and are not used internally by - inflate() or deflate(). The application can easily set up its own counters - updated after each call of inflate() or deflate() to count beyond 4 GB. - compress() and uncompress() may be limited to 4 GB, since they operate in a - single call. gzseek() and gztell() may be limited to 4 GB depending on how - zlib is compiled. See the zlibCompileFlags() function in zlib.h. - - The word "may" appears several times above since there is a 4 GB limit only - if the compiler's "long" type is 32 bits. If the compiler's "long" type is - 64 bits, then the limit is 16 exabytes. - -33. Does zlib have any security vulnerabilities? - - The only one that we are aware of is potentially in gzprintf(). If zlib is - compiled to use sprintf() or vsprintf(), then there is no protection - against a buffer overflow of an 8K string space (or other value as set by - gzbuffer()), other than the caller of gzprintf() assuring that the output - will not exceed 8K. On the other hand, if zlib is compiled to use - snprintf() or vsnprintf(), which should normally be the case, then there is - no vulnerability. The ./configure script will display warnings if an - insecure variation of sprintf() will be used by gzprintf(). Also the - zlibCompileFlags() function will return information on what variant of - sprintf() is used by gzprintf(). - - If you don't have snprintf() or vsnprintf() and would like one, you can - find a portable implementation here: - - http://www.ijs.si/software/snprintf/ - - Note that you should be using the most recent version of zlib. Versions - 1.1.3 and before were subject to a double-free vulnerability, and versions - 1.2.1 and 1.2.2 were subject to an access exception when decompressing - invalid compressed data. - -34. Is there a Java version of zlib? - - Probably what you want is to use zlib in Java. zlib is already included - as part of the Java SDK in the java.util.zip package. If you really want - a version of zlib written in the Java language, look on the zlib home - page for links: http://zlib.net/ . - -35. I get this or that compiler or source-code scanner warning when I crank it - up to maximally-pedantic. Can't you guys write proper code? - - Many years ago, we gave up attempting to avoid warnings on every compiler - in the universe. It just got to be a waste of time, and some compilers - were downright silly as well as contradicted each other. So now, we simply - make sure that the code always works. - -36. Valgrind (or some similar memory access checker) says that deflate is - performing a conditional jump that depends on an uninitialized value. - Isn't that a bug? - - No. That is intentional for performance reasons, and the output of deflate - is not affected. This only started showing up recently since zlib 1.2.x - uses malloc() by default for allocations, whereas earlier versions used - calloc(), which zeros out the allocated memory. Even though the code was - correct, versions 1.2.4 and later was changed to not stimulate these - checkers. - -37. Will zlib read the (insert any ancient or arcane format here) compressed - data format? - - Probably not. Look in the comp.compression FAQ for pointers to various - formats and associated software. - -38. How can I encrypt/decrypt zip files with zlib? - - zlib doesn't support encryption. The original PKZIP encryption is very - weak and can be broken with freely available programs. To get strong - encryption, use GnuPG, http://www.gnupg.org/ , which already includes zlib - compression. For PKZIP compatible "encryption", look at - http://www.info-zip.org/ - -39. What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings? - - "gzip" is the gzip format, and "deflate" is the zlib format. They should - probably have called the second one "zlib" instead to avoid confusion with - the raw deflate compressed data format. While the HTTP 1.1 RFC 2616 - correctly points to the zlib specification in RFC 1950 for the "deflate" - transfer encoding, there have been reports of servers and browsers that - incorrectly produce or expect raw deflate data per the deflate - specification in RFC 1951, most notably Microsoft. So even though the - "deflate" transfer encoding using the zlib format would be the more - efficient approach (and in fact exactly what the zlib format was designed - for), using the "gzip" transfer encoding is probably more reliable due to - an unfortunate choice of name on the part of the HTTP 1.1 authors. - - Bottom line: use the gzip format for HTTP 1.1 encoding. - -40. Does zlib support the new "Deflate64" format introduced by PKWare? - - No. PKWare has apparently decided to keep that format proprietary, since - they have not documented it as they have previous compression formats. In - any case, the compression improvements are so modest compared to other more - modern approaches, that it's not worth the effort to implement. - -41. I'm having a problem with the zip functions in zlib, can you help? - - There are no zip functions in zlib. You are probably using minizip by - Giles Vollant, which is found in the contrib directory of zlib. It is not - part of zlib. In fact none of the stuff in contrib is part of zlib. The - files in there are not supported by the zlib authors. You need to contact - the authors of the respective contribution for help. - -42. The match.asm code in contrib is under the GNU General Public License. - Since it's part of zlib, doesn't that mean that all of zlib falls under the - GNU GPL? - - No. The files in contrib are not part of zlib. They were contributed by - other authors and are provided as a convenience to the user within the zlib - distribution. Each item in contrib has its own license. - -43. Is zlib subject to export controls? What is its ECCN? - - zlib is not subject to export controls, and so is classified as EAR99. - -44. Can you please sign these lengthy legal documents and fax them back to us - so that we can use your software in our product? - - No. Go away. Shoo. diff --git a/contrib/zlib/INDEX b/contrib/zlib/INDEX deleted file mode 100644 index 2ba064120..000000000 --- a/contrib/zlib/INDEX +++ /dev/null @@ -1,68 +0,0 @@ -CMakeLists.txt cmake build file -ChangeLog history of changes -FAQ Frequently Asked Questions about zlib -INDEX this file -Makefile dummy Makefile that tells you to ./configure -Makefile.in template for Unix Makefile -README guess what -configure configure script for Unix -make_vms.com makefile for VMS -test/example.c zlib usages examples for build testing -test/minigzip.c minimal gzip-like functionality for build testing -test/infcover.c inf*.c code coverage for build coverage testing -treebuild.xml XML description of source file dependencies -zconf.h.cmakein zconf.h template for cmake -zconf.h.in zconf.h template for configure -zlib.3 Man page for zlib -zlib.3.pdf Man page in PDF format -zlib.map Linux symbol information -zlib.pc.in Template for pkg-config descriptor -zlib.pc.cmakein zlib.pc template for cmake -zlib2ansi perl script to convert source files for C++ compilation - -amiga/ makefiles for Amiga SAS C -as400/ makefiles for AS/400 -doc/ documentation for formats and algorithms -msdos/ makefiles for MSDOS -nintendods/ makefile for Nintendo DS -old/ makefiles for various architectures and zlib documentation - files that have not yet been updated for zlib 1.2.x -qnx/ makefiles for QNX -watcom/ makefiles for OpenWatcom -win32/ makefiles for Windows - - zlib public header files (required for library use): -zconf.h -zlib.h - - private source files used to build the zlib library: -adler32.c -compress.c -crc32.c -crc32.h -deflate.c -deflate.h -gzclose.c -gzguts.h -gzlib.c -gzread.c -gzwrite.c -infback.c -inffast.c -inffast.h -inffixed.h -inflate.c -inflate.h -inftrees.c -inftrees.h -trees.c -trees.h -uncompr.c -zutil.c -zutil.h - - source files for sample programs -See examples/README.examples - - unsupported contributions by third parties -See contrib/README.contrib diff --git a/contrib/zlib/Makefile.in b/contrib/zlib/Makefile.in deleted file mode 100644 index 3413266ae..000000000 --- a/contrib/zlib/Makefile.in +++ /dev/null @@ -1,410 +0,0 @@ -# Makefile for zlib -# Copyright (C) 1995-2017 Jean-loup Gailly, Mark Adler -# For conditions of distribution and use, see copyright notice in zlib.h - -# To compile and test, type: -# ./configure; make test -# Normally configure builds both a static and a shared library. -# If you want to build just a static library, use: ./configure --static - -# To use the asm code, type: -# cp contrib/asm?86/match.S ./match.S -# make LOC=-DASMV OBJA=match.o - -# To install /usr/local/lib/libz.* and /usr/local/include/zlib.h, type: -# make install -# To install in $HOME instead of /usr/local, use: -# make install prefix=$HOME - -CC=cc - -CFLAGS=-O -#CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7 -#CFLAGS=-g -DZLIB_DEBUG -#CFLAGS=-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \ -# -Wstrict-prototypes -Wmissing-prototypes - -SFLAGS=-O -LDFLAGS= -TEST_LDFLAGS=-L. libz.a -LDSHARED=$(CC) -CPP=$(CC) -E - -STATICLIB=libz.a -SHAREDLIB=libz.so -SHAREDLIBV=libz.so.1.2.11.1 -SHAREDLIBM=libz.so.1 -LIBS=$(STATICLIB) $(SHAREDLIBV) - -AR=ar -ARFLAGS=rc -RANLIB=ranlib -LDCONFIG=ldconfig -LDSHAREDLIBC=-lc -TAR=tar -SHELL=/bin/sh -EXE= - -prefix = /usr/local -exec_prefix = ${prefix} -libdir = ${exec_prefix}/lib -sharedlibdir = ${libdir} -includedir = ${prefix}/include -mandir = ${prefix}/share/man -man3dir = ${mandir}/man3 -pkgconfigdir = ${libdir}/pkgconfig -SRCDIR= -ZINC= -ZINCOUT=-I. - -OBJZ = adler32.o crc32.o deflate.o infback.o inffast.o inflate.o inftrees.o trees.o zutil.o -OBJG = compress.o uncompr.o gzclose.o gzlib.o gzread.o gzwrite.o -OBJC = $(OBJZ) $(OBJG) - -PIC_OBJZ = adler32.lo crc32.lo deflate.lo infback.lo inffast.lo inflate.lo inftrees.lo trees.lo zutil.lo -PIC_OBJG = compress.lo uncompr.lo gzclose.lo gzlib.lo gzread.lo gzwrite.lo -PIC_OBJC = $(PIC_OBJZ) $(PIC_OBJG) - -# to use the asm code: make OBJA=match.o, PIC_OBJA=match.lo -OBJA = -PIC_OBJA = - -OBJS = $(OBJC) $(OBJA) - -PIC_OBJS = $(PIC_OBJC) $(PIC_OBJA) - -all: static shared - -static: example$(EXE) minigzip$(EXE) - -shared: examplesh$(EXE) minigzipsh$(EXE) - -all64: example64$(EXE) minigzip64$(EXE) - -check: test - -test: all teststatic testshared - -teststatic: static - @TMPST=tmpst_$$; \ - if echo hello world | ./minigzip | ./minigzip -d && ./example $$TMPST ; then \ - echo ' *** zlib test OK ***'; \ - else \ - echo ' *** zlib test FAILED ***'; false; \ - fi - @rm -f tmpst_$$ - -testshared: shared - @LD_LIBRARY_PATH=`pwd`:$(LD_LIBRARY_PATH) ; export LD_LIBRARY_PATH; \ - LD_LIBRARYN32_PATH=`pwd`:$(LD_LIBRARYN32_PATH) ; export LD_LIBRARYN32_PATH; \ - DYLD_LIBRARY_PATH=`pwd`:$(DYLD_LIBRARY_PATH) ; export DYLD_LIBRARY_PATH; \ - SHLIB_PATH=`pwd`:$(SHLIB_PATH) ; export SHLIB_PATH; \ - TMPSH=tmpsh_$$; \ - if echo hello world | ./minigzipsh | ./minigzipsh -d && ./examplesh $$TMPSH; then \ - echo ' *** zlib shared test OK ***'; \ - else \ - echo ' *** zlib shared test FAILED ***'; false; \ - fi - @rm -f tmpsh_$$ - -test64: all64 - @TMP64=tmp64_$$; \ - if echo hello world | ./minigzip64 | ./minigzip64 -d && ./example64 $$TMP64; then \ - echo ' *** zlib 64-bit test OK ***'; \ - else \ - echo ' *** zlib 64-bit test FAILED ***'; false; \ - fi - @rm -f tmp64_$$ - -infcover.o: $(SRCDIR)test/infcover.c $(SRCDIR)zlib.h zconf.h - $(CC) $(CFLAGS) $(ZINCOUT) -c -o $@ $(SRCDIR)test/infcover.c - -infcover: infcover.o libz.a - $(CC) $(CFLAGS) -o $@ infcover.o libz.a - -cover: infcover - rm -f *.gcda - ./infcover - gcov inf*.c - -libz.a: $(OBJS) - $(AR) $(ARFLAGS) $@ $(OBJS) - -@ ($(RANLIB) $@ || true) >/dev/null 2>&1 - -match.o: match.S - $(CPP) match.S > _match.s - $(CC) -c _match.s - mv _match.o match.o - rm -f _match.s - -match.lo: match.S - $(CPP) match.S > _match.s - $(CC) -c -fPIC _match.s - mv _match.o match.lo - rm -f _match.s - -example.o: $(SRCDIR)test/example.c $(SRCDIR)zlib.h zconf.h - $(CC) $(CFLAGS) $(ZINCOUT) -c -o $@ $(SRCDIR)test/example.c - -minigzip.o: $(SRCDIR)test/minigzip.c $(SRCDIR)zlib.h zconf.h - $(CC) $(CFLAGS) $(ZINCOUT) -c -o $@ $(SRCDIR)test/minigzip.c - -example64.o: $(SRCDIR)test/example.c $(SRCDIR)zlib.h zconf.h - $(CC) $(CFLAGS) $(ZINCOUT) -D_FILE_OFFSET_BITS=64 -c -o $@ $(SRCDIR)test/example.c - -minigzip64.o: $(SRCDIR)test/minigzip.c $(SRCDIR)zlib.h zconf.h - $(CC) $(CFLAGS) $(ZINCOUT) -D_FILE_OFFSET_BITS=64 -c -o $@ $(SRCDIR)test/minigzip.c - - -adler32.o: $(SRCDIR)adler32.c - $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)adler32.c - -crc32.o: $(SRCDIR)crc32.c - $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)crc32.c - -deflate.o: $(SRCDIR)deflate.c - $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)deflate.c - -infback.o: $(SRCDIR)infback.c - $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)infback.c - -inffast.o: $(SRCDIR)inffast.c - $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)inffast.c - -inflate.o: $(SRCDIR)inflate.c - $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)inflate.c - -inftrees.o: $(SRCDIR)inftrees.c - $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)inftrees.c - -trees.o: $(SRCDIR)trees.c - $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)trees.c - -zutil.o: $(SRCDIR)zutil.c - $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)zutil.c - -compress.o: $(SRCDIR)compress.c - $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)compress.c - -uncompr.o: $(SRCDIR)uncompr.c - $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)uncompr.c - -gzclose.o: $(SRCDIR)gzclose.c - $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)gzclose.c - -gzlib.o: $(SRCDIR)gzlib.c - $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)gzlib.c - -gzread.o: $(SRCDIR)gzread.c - $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)gzread.c - -gzwrite.o: $(SRCDIR)gzwrite.c - $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)gzwrite.c - - -adler32.lo: $(SRCDIR)adler32.c - -@mkdir objs 2>/dev/null || test -d objs - $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/adler32.o $(SRCDIR)adler32.c - -@mv objs/adler32.o $@ - -crc32.lo: $(SRCDIR)crc32.c - -@mkdir objs 2>/dev/null || test -d objs - $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/crc32.o $(SRCDIR)crc32.c - -@mv objs/crc32.o $@ - -deflate.lo: $(SRCDIR)deflate.c - -@mkdir objs 2>/dev/null || test -d objs - $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/deflate.o $(SRCDIR)deflate.c - -@mv objs/deflate.o $@ - -infback.lo: $(SRCDIR)infback.c - -@mkdir objs 2>/dev/null || test -d objs - $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/infback.o $(SRCDIR)infback.c - -@mv objs/infback.o $@ - -inffast.lo: $(SRCDIR)inffast.c - -@mkdir objs 2>/dev/null || test -d objs - $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/inffast.o $(SRCDIR)inffast.c - -@mv objs/inffast.o $@ - -inflate.lo: $(SRCDIR)inflate.c - -@mkdir objs 2>/dev/null || test -d objs - $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/inflate.o $(SRCDIR)inflate.c - -@mv objs/inflate.o $@ - -inftrees.lo: $(SRCDIR)inftrees.c - -@mkdir objs 2>/dev/null || test -d objs - $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/inftrees.o $(SRCDIR)inftrees.c - -@mv objs/inftrees.o $@ - -trees.lo: $(SRCDIR)trees.c - -@mkdir objs 2>/dev/null || test -d objs - $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/trees.o $(SRCDIR)trees.c - -@mv objs/trees.o $@ - -zutil.lo: $(SRCDIR)zutil.c - -@mkdir objs 2>/dev/null || test -d objs - $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/zutil.o $(SRCDIR)zutil.c - -@mv objs/zutil.o $@ - -compress.lo: $(SRCDIR)compress.c - -@mkdir objs 2>/dev/null || test -d objs - $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/compress.o $(SRCDIR)compress.c - -@mv objs/compress.o $@ - -uncompr.lo: $(SRCDIR)uncompr.c - -@mkdir objs 2>/dev/null || test -d objs - $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/uncompr.o $(SRCDIR)uncompr.c - -@mv objs/uncompr.o $@ - -gzclose.lo: $(SRCDIR)gzclose.c - -@mkdir objs 2>/dev/null || test -d objs - $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/gzclose.o $(SRCDIR)gzclose.c - -@mv objs/gzclose.o $@ - -gzlib.lo: $(SRCDIR)gzlib.c - -@mkdir objs 2>/dev/null || test -d objs - $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/gzlib.o $(SRCDIR)gzlib.c - -@mv objs/gzlib.o $@ - -gzread.lo: $(SRCDIR)gzread.c - -@mkdir objs 2>/dev/null || test -d objs - $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/gzread.o $(SRCDIR)gzread.c - -@mv objs/gzread.o $@ - -gzwrite.lo: $(SRCDIR)gzwrite.c - -@mkdir objs 2>/dev/null || test -d objs - $(CC) $(SFLAGS) $(ZINC) -DPIC -c -o objs/gzwrite.o $(SRCDIR)gzwrite.c - -@mv objs/gzwrite.o $@ - - -placebo $(SHAREDLIBV): $(PIC_OBJS) libz.a - $(LDSHARED) $(SFLAGS) -o $@ $(PIC_OBJS) $(LDSHAREDLIBC) $(LDFLAGS) - rm -f $(SHAREDLIB) $(SHAREDLIBM) - ln -s $@ $(SHAREDLIB) - ln -s $@ $(SHAREDLIBM) - -@rmdir objs - -example$(EXE): example.o $(STATICLIB) - $(CC) $(CFLAGS) -o $@ example.o $(TEST_LDFLAGS) - -minigzip$(EXE): minigzip.o $(STATICLIB) - $(CC) $(CFLAGS) -o $@ minigzip.o $(TEST_LDFLAGS) - -examplesh$(EXE): example.o $(SHAREDLIBV) - $(CC) $(CFLAGS) -o $@ example.o -L. $(SHAREDLIBV) - -minigzipsh$(EXE): minigzip.o $(SHAREDLIBV) - $(CC) $(CFLAGS) -o $@ minigzip.o -L. $(SHAREDLIBV) - -example64$(EXE): example64.o $(STATICLIB) - $(CC) $(CFLAGS) -o $@ example64.o $(TEST_LDFLAGS) - -minigzip64$(EXE): minigzip64.o $(STATICLIB) - $(CC) $(CFLAGS) -o $@ minigzip64.o $(TEST_LDFLAGS) - -install-libs: $(LIBS) - -@if [ ! -d $(DESTDIR)$(exec_prefix) ]; then mkdir -p $(DESTDIR)$(exec_prefix); fi - -@if [ ! -d $(DESTDIR)$(libdir) ]; then mkdir -p $(DESTDIR)$(libdir); fi - -@if [ ! -d $(DESTDIR)$(sharedlibdir) ]; then mkdir -p $(DESTDIR)$(sharedlibdir); fi - -@if [ ! -d $(DESTDIR)$(man3dir) ]; then mkdir -p $(DESTDIR)$(man3dir); fi - -@if [ ! -d $(DESTDIR)$(pkgconfigdir) ]; then mkdir -p $(DESTDIR)$(pkgconfigdir); fi - rm -f $(DESTDIR)$(libdir)/$(STATICLIB) - cp $(STATICLIB) $(DESTDIR)$(libdir) - chmod 644 $(DESTDIR)$(libdir)/$(STATICLIB) - -@($(RANLIB) $(DESTDIR)$(libdir)/libz.a || true) >/dev/null 2>&1 - -@if test -n "$(SHAREDLIBV)"; then \ - rm -f $(DESTDIR)$(sharedlibdir)/$(SHAREDLIBV); \ - cp $(SHAREDLIBV) $(DESTDIR)$(sharedlibdir); \ - echo "cp $(SHAREDLIBV) $(DESTDIR)$(sharedlibdir)"; \ - chmod 755 $(DESTDIR)$(sharedlibdir)/$(SHAREDLIBV); \ - echo "chmod 755 $(DESTDIR)$(sharedlibdir)/$(SHAREDLIBV)"; \ - rm -f $(DESTDIR)$(sharedlibdir)/$(SHAREDLIB) $(DESTDIR)$(sharedlibdir)/$(SHAREDLIBM); \ - ln -s $(SHAREDLIBV) $(DESTDIR)$(sharedlibdir)/$(SHAREDLIB); \ - ln -s $(SHAREDLIBV) $(DESTDIR)$(sharedlibdir)/$(SHAREDLIBM); \ - ($(LDCONFIG) || true) >/dev/null 2>&1; \ - fi - rm -f $(DESTDIR)$(man3dir)/zlib.3 - cp $(SRCDIR)zlib.3 $(DESTDIR)$(man3dir) - chmod 644 $(DESTDIR)$(man3dir)/zlib.3 - rm -f $(DESTDIR)$(pkgconfigdir)/zlib.pc - cp zlib.pc $(DESTDIR)$(pkgconfigdir) - chmod 644 $(DESTDIR)$(pkgconfigdir)/zlib.pc -# The ranlib in install is needed on NeXTSTEP which checks file times -# ldconfig is for Linux - -install: install-libs - -@if [ ! -d $(DESTDIR)$(includedir) ]; then mkdir -p $(DESTDIR)$(includedir); fi - rm -f $(DESTDIR)$(includedir)/zlib.h $(DESTDIR)$(includedir)/zconf.h - cp $(SRCDIR)zlib.h zconf.h $(DESTDIR)$(includedir) - chmod 644 $(DESTDIR)$(includedir)/zlib.h $(DESTDIR)$(includedir)/zconf.h - -uninstall: - cd $(DESTDIR)$(includedir) && rm -f zlib.h zconf.h - cd $(DESTDIR)$(libdir) && rm -f libz.a; \ - if test -n "$(SHAREDLIBV)" -a -f $(SHAREDLIBV); then \ - rm -f $(SHAREDLIBV) $(SHAREDLIB) $(SHAREDLIBM); \ - fi - cd $(DESTDIR)$(man3dir) && rm -f zlib.3 - cd $(DESTDIR)$(pkgconfigdir) && rm -f zlib.pc - -docs: zlib.3.pdf - -zlib.3.pdf: $(SRCDIR)zlib.3 - groff -mandoc -f H -T ps $(SRCDIR)zlib.3 | ps2pdf - $@ - -zconf.h.cmakein: $(SRCDIR)zconf.h.in - -@ TEMPFILE=zconfh_$$; \ - echo "/#define ZCONF_H/ a\\\\\n#cmakedefine Z_PREFIX\\\\\n#cmakedefine Z_HAVE_UNISTD_H\n" >> $$TEMPFILE &&\ - sed -f $$TEMPFILE $(SRCDIR)zconf.h.in > $@ &&\ - touch -r $(SRCDIR)zconf.h.in $@ &&\ - rm $$TEMPFILE - -zconf: $(SRCDIR)zconf.h.in - cp -p $(SRCDIR)zconf.h.in zconf.h - -mostlyclean: clean -clean: - rm -f *.o *.lo *~ \ - example$(EXE) minigzip$(EXE) examplesh$(EXE) minigzipsh$(EXE) \ - example64$(EXE) minigzip64$(EXE) \ - infcover \ - libz.* foo.gz so_locations \ - _match.s maketree contrib/infback9/*.o - rm -rf objs - rm -f *.gcda *.gcno *.gcov - rm -f contrib/infback9/*.gcda contrib/infback9/*.gcno contrib/infback9/*.gcov - -maintainer-clean: distclean -distclean: clean zconf zconf.h.cmakein docs - rm -f Makefile zlib.pc configure.log - -@rm -f .DS_Store - @if [ -f Makefile.in ]; then \ - printf 'all:\n\t-@echo "Please use ./configure first. Thank you."\n' > Makefile ; \ - printf '\ndistclean:\n\tmake -f Makefile.in distclean\n' >> Makefile ; \ - touch -r $(SRCDIR)Makefile.in Makefile ; fi - @if [ ! -f zconf.h.in ]; then rm -f zconf.h zconf.h.cmakein ; fi - @if [ ! -f zlib.3 ]; then rm -f zlib.3.pdf ; fi - -tags: - etags $(SRCDIR)*.[ch] - -adler32.o zutil.o: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h -gzclose.o gzlib.o gzread.o gzwrite.o: $(SRCDIR)zlib.h zconf.h $(SRCDIR)gzguts.h -compress.o example.o minigzip.o uncompr.o: $(SRCDIR)zlib.h zconf.h -crc32.o: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)crc32.h -deflate.o: $(SRCDIR)deflate.h $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h -infback.o inflate.o: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h $(SRCDIR)inflate.h $(SRCDIR)inffast.h $(SRCDIR)inffixed.h -inffast.o: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h $(SRCDIR)inflate.h $(SRCDIR)inffast.h -inftrees.o: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h -trees.o: $(SRCDIR)deflate.h $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)trees.h - -adler32.lo zutil.lo: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h -gzclose.lo gzlib.lo gzread.lo gzwrite.lo: $(SRCDIR)zlib.h zconf.h $(SRCDIR)gzguts.h -compress.lo example.lo minigzip.lo uncompr.lo: $(SRCDIR)zlib.h zconf.h -crc32.lo: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)crc32.h -deflate.lo: $(SRCDIR)deflate.h $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h -infback.lo inflate.lo: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h $(SRCDIR)inflate.h $(SRCDIR)inffast.h $(SRCDIR)inffixed.h -inffast.lo: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h $(SRCDIR)inflate.h $(SRCDIR)inffast.h -inftrees.lo: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h -trees.lo: $(SRCDIR)deflate.h $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)trees.h diff --git a/contrib/zlib/configure b/contrib/zlib/configure deleted file mode 100644 index f4127954c..000000000 --- a/contrib/zlib/configure +++ /dev/null @@ -1,921 +0,0 @@ -#!/bin/sh -# configure script for zlib. -# -# Normally configure builds both a static and a shared library. -# If you want to build just a static library, use: ./configure --static -# -# To impose specific compiler or flags or install directory, use for example: -# prefix=$HOME CC=cc CFLAGS="-O4" ./configure -# or for csh/tcsh users: -# (setenv prefix $HOME; setenv CC cc; setenv CFLAGS "-O4"; ./configure) - -# Incorrect settings of CC or CFLAGS may prevent creating a shared library. -# If you have problems, try without defining CC and CFLAGS before reporting -# an error. - -# start off configure.log -echo -------------------- >> configure.log -echo $0 $* >> configure.log -date >> configure.log - -# get source directory -SRCDIR=`dirname $0` -if test $SRCDIR = "."; then - ZINC="" - ZINCOUT="-I." - SRCDIR="" -else - ZINC='-include zconf.h' - ZINCOUT='-I. -I$(SRCDIR)' - SRCDIR="$SRCDIR/" -fi - -# set command prefix for cross-compilation -if [ -n "${CHOST}" ]; then - uname="`echo "${CHOST}" | sed -e 's/^[^-]*-\([^-]*\)$/\1/' -e 's/^[^-]*-[^-]*-\([^-]*\)$/\1/' -e 's/^[^-]*-[^-]*-\([^-]*\)-.*$/\1/'`" - CROSS_PREFIX="${CHOST}-" -fi - -# destination name for static library -STATICLIB=libz.a - -# extract zlib version numbers from zlib.h -VER=`sed -n -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < ${SRCDIR}zlib.h` -VER3=`sed -n -e '/VERSION "/s/.*"\([0-9]*\\.[0-9]*\\.[0-9]*\).*/\1/p' < ${SRCDIR}zlib.h` -VER2=`sed -n -e '/VERSION "/s/.*"\([0-9]*\\.[0-9]*\)\\..*/\1/p' < ${SRCDIR}zlib.h` -VER1=`sed -n -e '/VERSION "/s/.*"\([0-9]*\)\\..*/\1/p' < ${SRCDIR}zlib.h` - -# establish commands for library building -if "${CROSS_PREFIX}ar" --version >/dev/null 2>/dev/null || test $? -lt 126; then - AR=${AR-"${CROSS_PREFIX}ar"} - test -n "${CROSS_PREFIX}" && echo Using ${AR} | tee -a configure.log -else - AR=${AR-"ar"} - test -n "${CROSS_PREFIX}" && echo Using ${AR} | tee -a configure.log -fi -ARFLAGS=${ARFLAGS-"rc"} -if "${CROSS_PREFIX}ranlib" --version >/dev/null 2>/dev/null || test $? -lt 126; then - RANLIB=${RANLIB-"${CROSS_PREFIX}ranlib"} - test -n "${CROSS_PREFIX}" && echo Using ${RANLIB} | tee -a configure.log -else - RANLIB=${RANLIB-"ranlib"} -fi -if "${CROSS_PREFIX}nm" --version >/dev/null 2>/dev/null || test $? -lt 126; then - NM=${NM-"${CROSS_PREFIX}nm"} - test -n "${CROSS_PREFIX}" && echo Using ${NM} | tee -a configure.log -else - NM=${NM-"nm"} -fi - -# set defaults before processing command line options -LDCONFIG=${LDCONFIG-"ldconfig"} -LDSHAREDLIBC="${LDSHAREDLIBC--lc}" -ARCHS= -prefix=${prefix-/usr/local} -exec_prefix=${exec_prefix-'${prefix}'} -libdir=${libdir-'${exec_prefix}/lib'} -sharedlibdir=${sharedlibdir-'${libdir}'} -includedir=${includedir-'${prefix}/include'} -mandir=${mandir-'${prefix}/share/man'} -shared_ext='.so' -shared=1 -solo=0 -cover=0 -zprefix=0 -zconst=0 -build64=0 -gcc=0 -warn=0 -debug=0 -old_cc="$CC" -old_cflags="$CFLAGS" -OBJC='$(OBJZ) $(OBJG)' -PIC_OBJC='$(PIC_OBJZ) $(PIC_OBJG)' - -# leave this script, optionally in a bad way -leave() -{ - if test "$*" != "0"; then - echo "** $0 aborting." | tee -a configure.log - fi - rm -f $test.[co] $test $test$shared_ext $test.gcno ./--version - echo -------------------- >> configure.log - echo >> configure.log - echo >> configure.log - exit $1 -} - -# process command line options -while test $# -ge 1 -do -case "$1" in - -h* | --help) - echo 'usage:' | tee -a configure.log - echo ' configure [--const] [--zprefix] [--prefix=PREFIX] [--eprefix=EXPREFIX]' | tee -a configure.log - echo ' [--static] [--64] [--libdir=LIBDIR] [--sharedlibdir=LIBDIR]' | tee -a configure.log - echo ' [--includedir=INCLUDEDIR] [--archs="-arch i386 -arch x86_64"]' | tee -a configure.log - exit 0 ;; - -p*=* | --prefix=*) prefix=`echo $1 | sed 's/.*=//'`; shift ;; - -e*=* | --eprefix=*) exec_prefix=`echo $1 | sed 's/.*=//'`; shift ;; - -l*=* | --libdir=*) libdir=`echo $1 | sed 's/.*=//'`; shift ;; - --sharedlibdir=*) sharedlibdir=`echo $1 | sed 's/.*=//'`; shift ;; - -i*=* | --includedir=*) includedir=`echo $1 | sed 's/.*=//'`;shift ;; - -u*=* | --uname=*) uname=`echo $1 | sed 's/.*=//'`;shift ;; - -p* | --prefix) prefix="$2"; shift; shift ;; - -e* | --eprefix) exec_prefix="$2"; shift; shift ;; - -l* | --libdir) libdir="$2"; shift; shift ;; - -i* | --includedir) includedir="$2"; shift; shift ;; - -s* | --shared | --enable-shared) shared=1; shift ;; - -t | --static) shared=0; shift ;; - --solo) solo=1; shift ;; - --cover) cover=1; shift ;; - -z* | --zprefix) zprefix=1; shift ;; - -6* | --64) build64=1; shift ;; - -a*=* | --archs=*) ARCHS=`echo $1 | sed 's/.*=//'`; shift ;; - --sysconfdir=*) echo "ignored option: --sysconfdir" | tee -a configure.log; shift ;; - --localstatedir=*) echo "ignored option: --localstatedir" | tee -a configure.log; shift ;; - -c* | --const) zconst=1; shift ;; - -w* | --warn) warn=1; shift ;; - -d* | --debug) debug=1; shift ;; - *) - echo "unknown option: $1" | tee -a configure.log - echo "$0 --help for help" | tee -a configure.log - leave 1;; - esac -done - -# temporary file name -test=ztest$$ - -# put arguments in log, also put test file in log if used in arguments -show() -{ - case "$*" in - *$test.c*) - echo === $test.c === >> configure.log - cat $test.c >> configure.log - echo === >> configure.log;; - esac - echo $* >> configure.log -} - -# check for gcc vs. cc and set compile and link flags based on the system identified by uname -cat > $test.c <&1` in - *gcc*) gcc=1 ;; - *clang*) gcc=1 ;; -esac - -show $cc -c $test.c -if test "$gcc" -eq 1 && ($cc -c $test.c) >> configure.log 2>&1; then - echo ... using gcc >> configure.log - CC="$cc" - CFLAGS="${CFLAGS--O3}" - SFLAGS="${CFLAGS--O3} -fPIC" - if test "$ARCHS"; then - CFLAGS="${CFLAGS} ${ARCHS}" - LDFLAGS="${LDFLAGS} ${ARCHS}" - fi - if test $build64 -eq 1; then - CFLAGS="${CFLAGS} -m64" - SFLAGS="${SFLAGS} -m64" - fi - if test "$warn" -eq 1; then - if test "$zconst" -eq 1; then - CFLAGS="${CFLAGS} -Wall -Wextra -Wcast-qual -pedantic -DZLIB_CONST -fsanitize=address" - else - CFLAGS="${CFLAGS} -Wall -Wextra -pedantic -fsanitize=address" - fi - fi - if test $debug -eq 1; then - CFLAGS="${CFLAGS} -DZLIB_DEBUG" - SFLAGS="${SFLAGS} -DZLIB_DEBUG" - fi - if test -z "$uname"; then - uname=`(uname -s || echo unknown) 2>/dev/null` - fi - case "$uname" in - Linux* | linux* | GNU | GNU/* | solaris*) - LDSHARED=${LDSHARED-"$cc -shared -Wl,-soname,libz.so.1,--version-script,${SRCDIR}zlib.map"} ;; - *BSD | *bsd* | DragonFly) - LDSHARED=${LDSHARED-"$cc -shared -Wl,-soname,libz.so.1,--version-script,${SRCDIR}zlib.map"} - LDCONFIG="ldconfig -m" ;; - CYGWIN* | Cygwin* | cygwin* | OS/2*) - EXE='.exe' ;; - MINGW* | mingw*) -# temporary bypass - rm -f $test.[co] $test $test$shared_ext - echo "Please use win32/Makefile.gcc instead." | tee -a configure.log - leave 1 - LDSHARED=${LDSHARED-"$cc -shared"} - LDSHAREDLIBC="" - EXE='.exe' ;; - QNX*) # This is for QNX6. I suppose that the QNX rule below is for QNX2,QNX4 - # (alain.bonnefoy@icbt.com) - LDSHARED=${LDSHARED-"$cc -shared -Wl,-hlibz.so.1"} ;; - HP-UX*) - LDSHARED=${LDSHARED-"$cc -shared $SFLAGS"} - case `(uname -m || echo unknown) 2>/dev/null` in - ia64) - shared_ext='.so' - SHAREDLIB='libz.so' ;; - *) - shared_ext='.sl' - SHAREDLIB='libz.sl' ;; - esac ;; - Darwin* | darwin*) - shared_ext='.dylib' - SHAREDLIB=libz$shared_ext - SHAREDLIBV=libz.$VER$shared_ext - SHAREDLIBM=libz.$VER1$shared_ext - LDSHARED=${LDSHARED-"$cc -dynamiclib -install_name $libdir/$SHAREDLIBM -compatibility_version $VER1 -current_version $VER3"} - if libtool -V 2>&1 | grep Apple > /dev/null; then - AR="libtool" - else - AR="/usr/bin/libtool" - fi - ARFLAGS="-o" ;; - *) LDSHARED=${LDSHARED-"$cc -shared"} ;; - esac -else - # find system name and corresponding cc options - CC=${CC-cc} - gcc=0 - echo ... using $CC >> configure.log - if test -z "$uname"; then - uname=`(uname -sr || echo unknown) 2>/dev/null` - fi - case "$uname" in - HP-UX*) SFLAGS=${CFLAGS-"-O +z"} - CFLAGS=${CFLAGS-"-O"} -# LDSHARED=${LDSHARED-"ld -b +vnocompatwarnings"} - LDSHARED=${LDSHARED-"ld -b"} - case `(uname -m || echo unknown) 2>/dev/null` in - ia64) - shared_ext='.so' - SHAREDLIB='libz.so' ;; - *) - shared_ext='.sl' - SHAREDLIB='libz.sl' ;; - esac ;; - IRIX*) SFLAGS=${CFLAGS-"-ansi -O2 -rpath ."} - CFLAGS=${CFLAGS-"-ansi -O2"} - LDSHARED=${LDSHARED-"cc -shared -Wl,-soname,libz.so.1"} ;; - OSF1\ V4*) SFLAGS=${CFLAGS-"-O -std1"} - CFLAGS=${CFLAGS-"-O -std1"} - LDFLAGS="${LDFLAGS} -Wl,-rpath,." - LDSHARED=${LDSHARED-"cc -shared -Wl,-soname,libz.so -Wl,-msym -Wl,-rpath,$(libdir) -Wl,-set_version,${VER}:1.0"} ;; - OSF1*) SFLAGS=${CFLAGS-"-O -std1"} - CFLAGS=${CFLAGS-"-O -std1"} - LDSHARED=${LDSHARED-"cc -shared -Wl,-soname,libz.so.1"} ;; - QNX*) SFLAGS=${CFLAGS-"-4 -O"} - CFLAGS=${CFLAGS-"-4 -O"} - LDSHARED=${LDSHARED-"cc"} - RANLIB=${RANLIB-"true"} - AR="cc" - ARFLAGS="-A" ;; - SCO_SV\ 3.2*) SFLAGS=${CFLAGS-"-O3 -dy -KPIC "} - CFLAGS=${CFLAGS-"-O3"} - LDSHARED=${LDSHARED-"cc -dy -KPIC -G"} ;; - SunOS\ 5* | solaris*) - LDSHARED=${LDSHARED-"cc -G -h libz$shared_ext.$VER1"} - SFLAGS=${CFLAGS-"-fast -KPIC"} - CFLAGS=${CFLAGS-"-fast"} - if test $build64 -eq 1; then - # old versions of SunPRO/Workshop/Studio don't support -m64, - # but newer ones do. Check for it. - flag64=`$CC -flags | egrep -- '^-m64'` - if test x"$flag64" != x"" ; then - CFLAGS="${CFLAGS} -m64" - SFLAGS="${SFLAGS} -m64" - else - case `(uname -m || echo unknown) 2>/dev/null` in - i86*) - SFLAGS="$SFLAGS -xarch=amd64" - CFLAGS="$CFLAGS -xarch=amd64" ;; - *) - SFLAGS="$SFLAGS -xarch=v9" - CFLAGS="$CFLAGS -xarch=v9" ;; - esac - fi - fi - if test -n "$ZINC"; then - ZINC='-I- -I. -I$(SRCDIR)' - fi - ;; - SunOS\ 4*) SFLAGS=${CFLAGS-"-O2 -PIC"} - CFLAGS=${CFLAGS-"-O2"} - LDSHARED=${LDSHARED-"ld"} ;; - SunStudio\ 9*) SFLAGS=${CFLAGS-"-fast -xcode=pic32 -xtarget=ultra3 -xarch=v9b"} - CFLAGS=${CFLAGS-"-fast -xtarget=ultra3 -xarch=v9b"} - LDSHARED=${LDSHARED-"cc -xarch=v9b"} ;; - UNIX_System_V\ 4.2.0) - SFLAGS=${CFLAGS-"-KPIC -O"} - CFLAGS=${CFLAGS-"-O"} - LDSHARED=${LDSHARED-"cc -G"} ;; - UNIX_SV\ 4.2MP) - SFLAGS=${CFLAGS-"-Kconform_pic -O"} - CFLAGS=${CFLAGS-"-O"} - LDSHARED=${LDSHARED-"cc -G"} ;; - OpenUNIX\ 5) - SFLAGS=${CFLAGS-"-KPIC -O"} - CFLAGS=${CFLAGS-"-O"} - LDSHARED=${LDSHARED-"cc -G"} ;; - AIX*) # Courtesy of dbakker@arrayasolutions.com - SFLAGS=${CFLAGS-"-O -qmaxmem=8192"} - CFLAGS=${CFLAGS-"-O -qmaxmem=8192"} - LDSHARED=${LDSHARED-"xlc -G"} ;; - # send working options for other systems to zlib@gzip.org - *) SFLAGS=${CFLAGS-"-O"} - CFLAGS=${CFLAGS-"-O"} - LDSHARED=${LDSHARED-"cc -shared"} ;; - esac -fi - -# destination names for shared library if not defined above -SHAREDLIB=${SHAREDLIB-"libz$shared_ext"} -SHAREDLIBV=${SHAREDLIBV-"libz$shared_ext.$VER"} -SHAREDLIBM=${SHAREDLIBM-"libz$shared_ext.$VER1"} - -echo >> configure.log - -# define functions for testing compiler and library characteristics and logging the results - -cat > $test.c </dev/null; then - try() - { - show $* - test "`( $* ) 2>&1 | tee -a configure.log`" = "" - } - echo - using any output from compiler to indicate an error >> configure.log -else - try() - { - show $* - ( $* ) >> configure.log 2>&1 - ret=$? - if test $ret -ne 0; then - echo "(exit code "$ret")" >> configure.log - fi - return $ret - } -fi - -tryboth() -{ - show $* - got=`( $* ) 2>&1` - ret=$? - printf %s "$got" >> configure.log - if test $ret -ne 0; then - return $ret - fi - test "$got" = "" -} - -cat > $test.c << EOF -int foo() { return 0; } -EOF -echo "Checking for obsessive-compulsive compiler options..." >> configure.log -if try $CC -c $CFLAGS $test.c; then - : -else - echo "Compiler error reporting is too harsh for $0 (perhaps remove -Werror)." | tee -a configure.log - leave 1 -fi - -echo >> configure.log - -# see if shared library build supported -cat > $test.c <> configure.log - show "$NM $test.o | grep _hello" - if test "`$NM $test.o | grep _hello | tee -a configure.log`" = ""; then - CPP="$CPP -DNO_UNDERLINE" - echo Checking for underline in external names... No. | tee -a configure.log - else - echo Checking for underline in external names... Yes. | tee -a configure.log - fi ;; -esac - -echo >> configure.log - -# check for size_t -cat > $test.c < -#include -size_t dummy = 0; -EOF -if try $CC -c $CFLAGS $test.c; then - echo "Checking for size_t... Yes." | tee -a configure.log - need_sizet=0 -else - echo "Checking for size_t... No." | tee -a configure.log - need_sizet=1 -fi - -echo >> configure.log - -# find the size_t integer type, if needed -if test $need_sizet -eq 1; then - cat > $test.c < $test.c < -int main(void) { - if (sizeof(void *) <= sizeof(int)) puts("int"); - else if (sizeof(void *) <= sizeof(long)) puts("long"); - else puts("z_longlong"); - return 0; -} -EOF - else - echo "Checking for long long... No." | tee -a configure.log - cat > $test.c < -int main(void) { - if (sizeof(void *) <= sizeof(int)) puts("int"); - else puts("long"); - return 0; -} -EOF - fi - if try $CC $CFLAGS -o $test $test.c; then - sizet=`./$test` - echo "Checking for a pointer-size integer type..." $sizet"." | tee -a configure.log - else - echo "Failed to find a pointer-size integer type." | tee -a configure.log - leave 1 - fi -fi - -if test $need_sizet -eq 1; then - CFLAGS="${CFLAGS} -DNO_SIZE_T=${sizet}" - SFLAGS="${SFLAGS} -DNO_SIZE_T=${sizet}" -fi - -echo >> configure.log - -# check for large file support, and if none, check for fseeko() -cat > $test.c < -off64_t dummy = 0; -EOF -if try $CC -c $CFLAGS -D_LARGEFILE64_SOURCE=1 $test.c; then - CFLAGS="${CFLAGS} -D_LARGEFILE64_SOURCE=1" - SFLAGS="${SFLAGS} -D_LARGEFILE64_SOURCE=1" - ALL="${ALL} all64" - TEST="${TEST} test64" - echo "Checking for off64_t... Yes." | tee -a configure.log - echo "Checking for fseeko... Yes." | tee -a configure.log -else - echo "Checking for off64_t... No." | tee -a configure.log - echo >> configure.log - cat > $test.c < -int main(void) { - fseeko(NULL, 0, 0); - return 0; -} -EOF - if try $CC $CFLAGS -o $test $test.c; then - echo "Checking for fseeko... Yes." | tee -a configure.log - else - CFLAGS="${CFLAGS} -DNO_FSEEKO" - SFLAGS="${SFLAGS} -DNO_FSEEKO" - echo "Checking for fseeko... No." | tee -a configure.log - fi -fi - -echo >> configure.log - -# check for strerror() for use by gz* functions -cat > $test.c < -#include -int main() { return strlen(strerror(errno)); } -EOF -if try $CC $CFLAGS -o $test $test.c; then - echo "Checking for strerror... Yes." | tee -a configure.log -else - CFLAGS="${CFLAGS} -DNO_STRERROR" - SFLAGS="${SFLAGS} -DNO_STRERROR" - echo "Checking for strerror... No." | tee -a configure.log -fi - -# copy clean zconf.h for subsequent edits -cp -p ${SRCDIR}zconf.h.in zconf.h - -echo >> configure.log - -# check for unistd.h and save result in zconf.h -cat > $test.c < -int main() { return 0; } -EOF -if try $CC -c $CFLAGS $test.c; then - sed < zconf.h "/^#ifdef HAVE_UNISTD_H.* may be/s/def HAVE_UNISTD_H\(.*\) may be/ 1\1 was/" > zconf.temp.h - mv zconf.temp.h zconf.h - echo "Checking for unistd.h... Yes." | tee -a configure.log -else - echo "Checking for unistd.h... No." | tee -a configure.log -fi - -echo >> configure.log - -# check for stdarg.h and save result in zconf.h -cat > $test.c < -int main() { return 0; } -EOF -if try $CC -c $CFLAGS $test.c; then - sed < zconf.h "/^#ifdef HAVE_STDARG_H.* may be/s/def HAVE_STDARG_H\(.*\) may be/ 1\1 was/" > zconf.temp.h - mv zconf.temp.h zconf.h - echo "Checking for stdarg.h... Yes." | tee -a configure.log -else - echo "Checking for stdarg.h... No." | tee -a configure.log -fi - -# if the z_ prefix was requested, save that in zconf.h -if test $zprefix -eq 1; then - sed < zconf.h "/#ifdef Z_PREFIX.* may be/s/def Z_PREFIX\(.*\) may be/ 1\1 was/" > zconf.temp.h - mv zconf.temp.h zconf.h - echo >> configure.log - echo "Using z_ prefix on all symbols." | tee -a configure.log -fi - -# if --solo compilation was requested, save that in zconf.h and remove gz stuff from object lists -if test $solo -eq 1; then - sed '/#define ZCONF_H/a\ -#define Z_SOLO - -' < zconf.h > zconf.temp.h - mv zconf.temp.h zconf.h -OBJC='$(OBJZ)' -PIC_OBJC='$(PIC_OBJZ)' -fi - -# if code coverage testing was requested, use older gcc if defined, e.g. "gcc-4.2" on Mac OS X -if test $cover -eq 1; then - CFLAGS="${CFLAGS} -fprofile-arcs -ftest-coverage" - if test -n "$GCC_CLASSIC"; then - CC=$GCC_CLASSIC - fi -fi - -echo >> configure.log - -# conduct a series of tests to resolve eight possible cases of using "vs" or "s" printf functions -# (using stdarg or not), with or without "n" (proving size of buffer), and with or without a -# return value. The most secure result is vsnprintf() with a return value. snprintf() with a -# return value is secure as well, but then gzprintf() will be limited to 20 arguments. -cat > $test.c < -#include -#include "zconf.h" -int main() -{ -#ifndef STDC - choke me -#endif - return 0; -} -EOF -if try $CC -c $CFLAGS $test.c; then - echo "Checking whether to use vs[n]printf() or s[n]printf()... using vs[n]printf()." | tee -a configure.log - - echo >> configure.log - cat > $test.c < -#include -int mytest(const char *fmt, ...) -{ - char buf[20]; - va_list ap; - va_start(ap, fmt); - vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - return 0; -} -int main() -{ - return (mytest("Hello%d\n", 1)); -} -EOF - if try $CC $CFLAGS -o $test $test.c; then - echo "Checking for vsnprintf() in stdio.h... Yes." | tee -a configure.log - - echo >> configure.log - cat >$test.c < -#include -int mytest(const char *fmt, ...) -{ - int n; - char buf[20]; - va_list ap; - va_start(ap, fmt); - n = vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - return n; -} -int main() -{ - return (mytest("Hello%d\n", 1)); -} -EOF - - if try $CC -c $CFLAGS $test.c; then - echo "Checking for return value of vsnprintf()... Yes." | tee -a configure.log - else - CFLAGS="$CFLAGS -DHAS_vsnprintf_void" - SFLAGS="$SFLAGS -DHAS_vsnprintf_void" - echo "Checking for return value of vsnprintf()... No." | tee -a configure.log - echo " WARNING: apparently vsnprintf() does not return a value. zlib" | tee -a configure.log - echo " can build but will be open to possible string-format security" | tee -a configure.log - echo " vulnerabilities." | tee -a configure.log - fi - else - CFLAGS="$CFLAGS -DNO_vsnprintf" - SFLAGS="$SFLAGS -DNO_vsnprintf" - echo "Checking for vsnprintf() in stdio.h... No." | tee -a configure.log - echo " WARNING: vsnprintf() not found, falling back to vsprintf(). zlib" | tee -a configure.log - echo " can build but will be open to possible buffer-overflow security" | tee -a configure.log - echo " vulnerabilities." | tee -a configure.log - - echo >> configure.log - cat >$test.c < -#include -int mytest(const char *fmt, ...) -{ - int n; - char buf[20]; - va_list ap; - va_start(ap, fmt); - n = vsprintf(buf, fmt, ap); - va_end(ap); - return n; -} -int main() -{ - return (mytest("Hello%d\n", 1)); -} -EOF - - if try $CC -c $CFLAGS $test.c; then - echo "Checking for return value of vsprintf()... Yes." | tee -a configure.log - else - CFLAGS="$CFLAGS -DHAS_vsprintf_void" - SFLAGS="$SFLAGS -DHAS_vsprintf_void" - echo "Checking for return value of vsprintf()... No." | tee -a configure.log - echo " WARNING: apparently vsprintf() does not return a value. zlib" | tee -a configure.log - echo " can build but will be open to possible string-format security" | tee -a configure.log - echo " vulnerabilities." | tee -a configure.log - fi - fi -else - echo "Checking whether to use vs[n]printf() or s[n]printf()... using s[n]printf()." | tee -a configure.log - - echo >> configure.log - cat >$test.c < -int mytest() -{ - char buf[20]; - snprintf(buf, sizeof(buf), "%s", "foo"); - return 0; -} -int main() -{ - return (mytest()); -} -EOF - - if try $CC $CFLAGS -o $test $test.c; then - echo "Checking for snprintf() in stdio.h... Yes." | tee -a configure.log - - echo >> configure.log - cat >$test.c < -int mytest() -{ - char buf[20]; - return snprintf(buf, sizeof(buf), "%s", "foo"); -} -int main() -{ - return (mytest()); -} -EOF - - if try $CC -c $CFLAGS $test.c; then - echo "Checking for return value of snprintf()... Yes." | tee -a configure.log - else - CFLAGS="$CFLAGS -DHAS_snprintf_void" - SFLAGS="$SFLAGS -DHAS_snprintf_void" - echo "Checking for return value of snprintf()... No." | tee -a configure.log - echo " WARNING: apparently snprintf() does not return a value. zlib" | tee -a configure.log - echo " can build but will be open to possible string-format security" | tee -a configure.log - echo " vulnerabilities." | tee -a configure.log - fi - else - CFLAGS="$CFLAGS -DNO_snprintf" - SFLAGS="$SFLAGS -DNO_snprintf" - echo "Checking for snprintf() in stdio.h... No." | tee -a configure.log - echo " WARNING: snprintf() not found, falling back to sprintf(). zlib" | tee -a configure.log - echo " can build but will be open to possible buffer-overflow security" | tee -a configure.log - echo " vulnerabilities." | tee -a configure.log - - echo >> configure.log - cat >$test.c < -int mytest() -{ - char buf[20]; - return sprintf(buf, "%s", "foo"); -} -int main() -{ - return (mytest()); -} -EOF - - if try $CC -c $CFLAGS $test.c; then - echo "Checking for return value of sprintf()... Yes." | tee -a configure.log - else - CFLAGS="$CFLAGS -DHAS_sprintf_void" - SFLAGS="$SFLAGS -DHAS_sprintf_void" - echo "Checking for return value of sprintf()... No." | tee -a configure.log - echo " WARNING: apparently sprintf() does not return a value. zlib" | tee -a configure.log - echo " can build but will be open to possible string-format security" | tee -a configure.log - echo " vulnerabilities." | tee -a configure.log - fi - fi -fi - -# see if we can hide zlib internal symbols that are linked between separate source files -if test "$gcc" -eq 1; then - echo >> configure.log - cat > $test.c <> configure.log -echo ALL = $ALL >> configure.log -echo AR = $AR >> configure.log -echo ARFLAGS = $ARFLAGS >> configure.log -echo CC = $CC >> configure.log -echo CFLAGS = $CFLAGS >> configure.log -echo CPP = $CPP >> configure.log -echo EXE = $EXE >> configure.log -echo LDCONFIG = $LDCONFIG >> configure.log -echo LDFLAGS = $LDFLAGS >> configure.log -echo LDSHARED = $LDSHARED >> configure.log -echo LDSHAREDLIBC = $LDSHAREDLIBC >> configure.log -echo OBJC = $OBJC >> configure.log -echo PIC_OBJC = $PIC_OBJC >> configure.log -echo RANLIB = $RANLIB >> configure.log -echo SFLAGS = $SFLAGS >> configure.log -echo SHAREDLIB = $SHAREDLIB >> configure.log -echo SHAREDLIBM = $SHAREDLIBM >> configure.log -echo SHAREDLIBV = $SHAREDLIBV >> configure.log -echo STATICLIB = $STATICLIB >> configure.log -echo TEST = $TEST >> configure.log -echo VER = $VER >> configure.log -echo Z_U4 = $Z_U4 >> configure.log -echo SRCDIR = $SRCDIR >> configure.log -echo exec_prefix = $exec_prefix >> configure.log -echo includedir = $includedir >> configure.log -echo libdir = $libdir >> configure.log -echo mandir = $mandir >> configure.log -echo prefix = $prefix >> configure.log -echo sharedlibdir = $sharedlibdir >> configure.log -echo uname = $uname >> configure.log - -# udpate Makefile with the configure results -sed < ${SRCDIR}Makefile.in " -/^CC *=/s#=.*#=$CC# -/^CFLAGS *=/s#=.*#=$CFLAGS# -/^SFLAGS *=/s#=.*#=$SFLAGS# -/^LDFLAGS *=/s#=.*#=$LDFLAGS# -/^LDSHARED *=/s#=.*#=$LDSHARED# -/^CPP *=/s#=.*#=$CPP# -/^STATICLIB *=/s#=.*#=$STATICLIB# -/^SHAREDLIB *=/s#=.*#=$SHAREDLIB# -/^SHAREDLIBV *=/s#=.*#=$SHAREDLIBV# -/^SHAREDLIBM *=/s#=.*#=$SHAREDLIBM# -/^AR *=/s#=.*#=$AR# -/^ARFLAGS *=/s#=.*#=$ARFLAGS# -/^RANLIB *=/s#=.*#=$RANLIB# -/^LDCONFIG *=/s#=.*#=$LDCONFIG# -/^LDSHAREDLIBC *=/s#=.*#=$LDSHAREDLIBC# -/^EXE *=/s#=.*#=$EXE# -/^SRCDIR *=/s#=.*#=$SRCDIR# -/^ZINC *=/s#=.*#=$ZINC# -/^ZINCOUT *=/s#=.*#=$ZINCOUT# -/^prefix *=/s#=.*#=$prefix# -/^exec_prefix *=/s#=.*#=$exec_prefix# -/^libdir *=/s#=.*#=$libdir# -/^sharedlibdir *=/s#=.*#=$sharedlibdir# -/^includedir *=/s#=.*#=$includedir# -/^mandir *=/s#=.*#=$mandir# -/^OBJC *=/s#=.*#= $OBJC# -/^PIC_OBJC *=/s#=.*#= $PIC_OBJC# -/^all: */s#:.*#: $ALL# -/^test: */s#:.*#: $TEST# -" > Makefile - -# create zlib.pc with the configure results -sed < ${SRCDIR}zlib.pc.in " -/^CC *=/s#=.*#=$CC# -/^CFLAGS *=/s#=.*#=$CFLAGS# -/^CPP *=/s#=.*#=$CPP# -/^LDSHARED *=/s#=.*#=$LDSHARED# -/^STATICLIB *=/s#=.*#=$STATICLIB# -/^SHAREDLIB *=/s#=.*#=$SHAREDLIB# -/^SHAREDLIBV *=/s#=.*#=$SHAREDLIBV# -/^SHAREDLIBM *=/s#=.*#=$SHAREDLIBM# -/^AR *=/s#=.*#=$AR# -/^ARFLAGS *=/s#=.*#=$ARFLAGS# -/^RANLIB *=/s#=.*#=$RANLIB# -/^EXE *=/s#=.*#=$EXE# -/^prefix *=/s#=.*#=$prefix# -/^exec_prefix *=/s#=.*#=$exec_prefix# -/^libdir *=/s#=.*#=$libdir# -/^sharedlibdir *=/s#=.*#=$sharedlibdir# -/^includedir *=/s#=.*#=$includedir# -/^mandir *=/s#=.*#=$mandir# -/^LDFLAGS *=/s#=.*#=$LDFLAGS# -" | sed -e " -s/\@VERSION\@/$VER/g; -" > zlib.pc - -# done -leave 0 diff --git a/contrib/zlib/make_vms.com b/contrib/zlib/make_vms.com deleted file mode 100644 index 65e9d0cbc..000000000 --- a/contrib/zlib/make_vms.com +++ /dev/null @@ -1,867 +0,0 @@ -$! make libz under VMS written by -$! Martin P.J. Zinser -$! -$! In case of problems with the install you might contact me at -$! zinser@zinser.no-ip.info(preferred) or -$! martin.zinser@eurexchange.com (work) -$! -$! Make procedure history for Zlib -$! -$!------------------------------------------------------------------------------ -$! Version history -$! 0.01 20060120 First version to receive a number -$! 0.02 20061008 Adapt to new Makefile.in -$! 0.03 20091224 Add support for large file check -$! 0.04 20100110 Add new gzclose, gzlib, gzread, gzwrite -$! 0.05 20100221 Exchange zlibdefs.h by zconf.h.in -$! 0.06 20120111 Fix missing amiss_err, update zconf_h.in, fix new exmples -$! subdir path, update module search in makefile.in -$! 0.07 20120115 Triggered by work done by Alexey Chupahin completly redesigned -$! shared image creation -$! 0.08 20120219 Make it work on VAX again, pre-load missing symbols to shared -$! image -$! 0.09 20120305 SMS. P1 sets builder ("MMK", "MMS", " " (built-in)). -$! "" -> automatic, preference: MMK, MMS, built-in. -$! -$ on error then goto err_exit -$! -$ true = 1 -$ false = 0 -$ tmpnam = "temp_" + f$getjpi("","pid") -$ tt = tmpnam + ".txt" -$ tc = tmpnam + ".c" -$ th = tmpnam + ".h" -$ define/nolog tconfig 'th' -$ its_decc = false -$ its_vaxc = false -$ its_gnuc = false -$ s_case = False -$! -$! Setup variables holding "config" information -$! -$ Make = "''p1'" -$ name = "Zlib" -$ version = "?.?.?" -$ v_string = "ZLIB_VERSION" -$ v_file = "zlib.h" -$ ccopt = "/include = []" -$ lopts = "" -$ dnsrl = "" -$ aconf_in_file = "zconf.h.in#zconf.h_in#zconf_h.in" -$ conf_check_string = "" -$ linkonly = false -$ optfile = name + ".opt" -$ mapfile = name + ".map" -$ libdefs = "" -$ vax = f$getsyi("HW_MODEL").lt.1024 -$ axp = f$getsyi("HW_MODEL").ge.1024 .and. f$getsyi("HW_MODEL").lt.4096 -$ ia64 = f$getsyi("HW_MODEL").ge.4096 -$! -$! 2012-03-05 SMS. -$! Why is this needed? And if it is needed, why not simply ".not. vax"? -$! -$!!! if axp .or. ia64 then set proc/parse=extended -$! -$ whoami = f$parse(f$environment("Procedure"),,,,"NO_CONCEAL") -$ mydef = F$parse(whoami,,,"DEVICE") -$ mydir = f$parse(whoami,,,"DIRECTORY") - "][" -$ myproc = f$parse(whoami,,,"Name") + f$parse(whoami,,,"type") -$! -$! Check for MMK/MMS -$! -$ if (Make .eqs. "") -$ then -$ If F$Search ("Sys$System:MMS.EXE") .nes. "" Then Make = "MMS" -$ If F$Type (MMK) .eqs. "STRING" Then Make = "MMK" -$ else -$ Make = f$edit( Make, "trim") -$ endif -$! -$ gosub find_version -$! -$ open/write topt tmp.opt -$ open/write optf 'optfile' -$! -$ gosub check_opts -$! -$! Look for the compiler used -$! -$ gosub check_compiler -$ close topt -$ close optf -$! -$ if its_decc -$ then -$ ccopt = "/prefix=all" + ccopt -$ if f$trnlnm("SYS") .eqs. "" -$ then -$ if axp -$ then -$ define sys sys$library: -$ else -$ ccopt = "/decc" + ccopt -$ define sys decc$library_include: -$ endif -$ endif -$! -$! 2012-03-05 SMS. -$! Why /NAMES = AS_IS? Why not simply ".not. vax"? And why not on VAX? -$! -$ if axp .or. ia64 -$ then -$ ccopt = ccopt + "/name=as_is/opt=(inline=speed)" -$ s_case = true -$ endif -$ endif -$ if its_vaxc .or. its_gnuc -$ then -$ if f$trnlnm("SYS").eqs."" then define sys sys$library: -$ endif -$! -$! Build a fake configure input header -$! -$ open/write conf_hin config.hin -$ write conf_hin "#undef _LARGEFILE64_SOURCE" -$ close conf_hin -$! -$! -$ i = 0 -$FIND_ACONF: -$ fname = f$element(i,"#",aconf_in_file) -$ if fname .eqs. "#" then goto AMISS_ERR -$ if f$search(fname) .eqs. "" -$ then -$ i = i + 1 -$ goto find_aconf -$ endif -$ open/read/err=aconf_err aconf_in 'fname' -$ open/write aconf zconf.h -$ACONF_LOOP: -$ read/end_of_file=aconf_exit aconf_in line -$ work = f$edit(line, "compress,trim") -$ if f$extract(0,6,work) .nes. "#undef" -$ then -$ if f$extract(0,12,work) .nes. "#cmakedefine" -$ then -$ write aconf line -$ endif -$ else -$ cdef = f$element(1," ",work) -$ gosub check_config -$ endif -$ goto aconf_loop -$ACONF_EXIT: -$ write aconf "" -$ write aconf "/* VMS specifics added by make_vms.com: */" -$ write aconf "#define VMS 1" -$ write aconf "#include " -$ write aconf "#include " -$ write aconf "#ifdef _LARGEFILE" -$ write aconf "# define off64_t __off64_t" -$ write aconf "# define fopen64 fopen" -$ write aconf "# define fseeko64 fseeko" -$ write aconf "# define lseek64 lseek" -$ write aconf "# define ftello64 ftell" -$ write aconf "#endif" -$ write aconf "#if !defined( __VAX) && (__CRTL_VER >= 70312000)" -$ write aconf "# define HAVE_VSNPRINTF" -$ write aconf "#endif" -$ close aconf_in -$ close aconf -$ if f$search("''th'") .nes. "" then delete 'th';* -$! Build the thing plain or with mms -$! -$ write sys$output "Compiling Zlib sources ..." -$ if make.eqs."" -$ then -$ if (f$search( "example.obj;*") .nes. "") then delete example.obj;* -$ if (f$search( "minigzip.obj;*") .nes. "") then delete minigzip.obj;* -$ CALL MAKE adler32.OBJ "CC ''CCOPT' adler32" - - adler32.c zlib.h zconf.h -$ CALL MAKE compress.OBJ "CC ''CCOPT' compress" - - compress.c zlib.h zconf.h -$ CALL MAKE crc32.OBJ "CC ''CCOPT' crc32" - - crc32.c zlib.h zconf.h -$ CALL MAKE deflate.OBJ "CC ''CCOPT' deflate" - - deflate.c deflate.h zutil.h zlib.h zconf.h -$ CALL MAKE gzclose.OBJ "CC ''CCOPT' gzclose" - - gzclose.c zutil.h zlib.h zconf.h -$ CALL MAKE gzlib.OBJ "CC ''CCOPT' gzlib" - - gzlib.c zutil.h zlib.h zconf.h -$ CALL MAKE gzread.OBJ "CC ''CCOPT' gzread" - - gzread.c zutil.h zlib.h zconf.h -$ CALL MAKE gzwrite.OBJ "CC ''CCOPT' gzwrite" - - gzwrite.c zutil.h zlib.h zconf.h -$ CALL MAKE infback.OBJ "CC ''CCOPT' infback" - - infback.c zutil.h inftrees.h inflate.h inffast.h inffixed.h -$ CALL MAKE inffast.OBJ "CC ''CCOPT' inffast" - - inffast.c zutil.h zlib.h zconf.h inffast.h -$ CALL MAKE inflate.OBJ "CC ''CCOPT' inflate" - - inflate.c zutil.h zlib.h zconf.h infblock.h -$ CALL MAKE inftrees.OBJ "CC ''CCOPT' inftrees" - - inftrees.c zutil.h zlib.h zconf.h inftrees.h -$ CALL MAKE trees.OBJ "CC ''CCOPT' trees" - - trees.c deflate.h zutil.h zlib.h zconf.h -$ CALL MAKE uncompr.OBJ "CC ''CCOPT' uncompr" - - uncompr.c zlib.h zconf.h -$ CALL MAKE zutil.OBJ "CC ''CCOPT' zutil" - - zutil.c zutil.h zlib.h zconf.h -$ write sys$output "Building Zlib ..." -$ CALL MAKE libz.OLB "lib/crea libz.olb *.obj" *.OBJ -$ write sys$output "Building example..." -$ CALL MAKE example.OBJ "CC ''CCOPT' [.test]example" - - [.test]example.c zlib.h zconf.h -$ call make example.exe "LINK example,libz.olb/lib" example.obj libz.olb -$ write sys$output "Building minigzip..." -$ CALL MAKE minigzip.OBJ "CC ''CCOPT' [.test]minigzip" - - [.test]minigzip.c zlib.h zconf.h -$ call make minigzip.exe - - "LINK minigzip,libz.olb/lib" - - minigzip.obj libz.olb -$ else -$ gosub crea_mms -$ write sys$output "Make ''name' ''version' with ''Make' " -$ 'make' -$ endif -$! -$! Create shareable image -$! -$ gosub crea_olist -$ write sys$output "Creating libzshr.exe" -$ call map_2_shopt 'mapfile' 'optfile' -$ LINK_'lopts'/SHARE=libzshr.exe modules.opt/opt,'optfile'/opt -$ write sys$output "Zlib build completed" -$ delete/nolog tmp.opt;* -$ exit -$AMISS_ERR: -$ write sys$output "No source for config.hin found." -$ write sys$output "Tried any of ''aconf_in_file'" -$ goto err_exit -$CC_ERR: -$ write sys$output "C compiler required to build ''name'" -$ goto err_exit -$ERR_EXIT: -$ set message/facil/ident/sever/text -$ close/nolog optf -$ close/nolog topt -$ close/nolog aconf_in -$ close/nolog aconf -$ close/nolog out -$ close/nolog min -$ close/nolog mod -$ close/nolog h_in -$ write sys$output "Exiting..." -$ exit 2 -$! -$! -$MAKE: SUBROUTINE !SUBROUTINE TO CHECK DEPENDENCIES -$ V = 'F$Verify(0) -$! P1 = What we are trying to make -$! P2 = Command to make it -$! P3 - P8 What it depends on -$ -$ If F$Search(P1) .Eqs. "" Then Goto Makeit -$ Time = F$CvTime(F$File(P1,"RDT")) -$arg=3 -$Loop: -$ Argument = P'arg -$ If Argument .Eqs. "" Then Goto Exit -$ El=0 -$Loop2: -$ File = F$Element(El," ",Argument) -$ If File .Eqs. " " Then Goto Endl -$ AFile = "" -$Loop3: -$ OFile = AFile -$ AFile = F$Search(File) -$ If AFile .Eqs. "" .Or. AFile .Eqs. OFile Then Goto NextEl -$ If F$CvTime(F$File(AFile,"RDT")) .Ges. Time Then Goto Makeit -$ Goto Loop3 -$NextEL: -$ El = El + 1 -$ Goto Loop2 -$EndL: -$ arg=arg+1 -$ If arg .Le. 8 Then Goto Loop -$ Goto Exit -$ -$Makeit: -$ VV=F$VERIFY(0) -$ write sys$output P2 -$ 'P2 -$ VV='F$Verify(VV) -$Exit: -$ If V Then Set Verify -$ENDSUBROUTINE -$!------------------------------------------------------------------------------ -$! -$! Check command line options and set symbols accordingly -$! -$!------------------------------------------------------------------------------ -$! Version history -$! 0.01 20041206 First version to receive a number -$! 0.02 20060126 Add new "HELP" target -$ CHECK_OPTS: -$ i = 1 -$ OPT_LOOP: -$ if i .lt. 9 -$ then -$ cparm = f$edit(p'i',"upcase") -$! -$! Check if parameter actually contains something -$! -$ if f$edit(cparm,"trim") .nes. "" -$ then -$ if cparm .eqs. "DEBUG" -$ then -$ ccopt = ccopt + "/noopt/deb" -$ lopts = lopts + "/deb" -$ endif -$ if f$locate("CCOPT=",cparm) .lt. f$length(cparm) -$ then -$ start = f$locate("=",cparm) + 1 -$ len = f$length(cparm) - start -$ ccopt = ccopt + f$extract(start,len,cparm) -$ if f$locate("AS_IS",f$edit(ccopt,"UPCASE")) .lt. f$length(ccopt) - - then s_case = true -$ endif -$ if cparm .eqs. "LINK" then linkonly = true -$ if f$locate("LOPTS=",cparm) .lt. f$length(cparm) -$ then -$ start = f$locate("=",cparm) + 1 -$ len = f$length(cparm) - start -$ lopts = lopts + f$extract(start,len,cparm) -$ endif -$ if f$locate("CC=",cparm) .lt. f$length(cparm) -$ then -$ start = f$locate("=",cparm) + 1 -$ len = f$length(cparm) - start -$ cc_com = f$extract(start,len,cparm) - if (cc_com .nes. "DECC") .and. - - (cc_com .nes. "VAXC") .and. - - (cc_com .nes. "GNUC") -$ then -$ write sys$output "Unsupported compiler choice ''cc_com' ignored" -$ write sys$output "Use DECC, VAXC, or GNUC instead" -$ else -$ if cc_com .eqs. "DECC" then its_decc = true -$ if cc_com .eqs. "VAXC" then its_vaxc = true -$ if cc_com .eqs. "GNUC" then its_gnuc = true -$ endif -$ endif -$ if f$locate("MAKE=",cparm) .lt. f$length(cparm) -$ then -$ start = f$locate("=",cparm) + 1 -$ len = f$length(cparm) - start -$ mmks = f$extract(start,len,cparm) -$ if (mmks .eqs. "MMK") .or. (mmks .eqs. "MMS") -$ then -$ make = mmks -$ else -$ write sys$output "Unsupported make choice ''mmks' ignored" -$ write sys$output "Use MMK or MMS instead" -$ endif -$ endif -$ if cparm .eqs. "HELP" then gosub bhelp -$ endif -$ i = i + 1 -$ goto opt_loop -$ endif -$ return -$!------------------------------------------------------------------------------ -$! -$! Look for the compiler used -$! -$! Version history -$! 0.01 20040223 First version to receive a number -$! 0.02 20040229 Save/set value of decc$no_rooted_search_lists -$! 0.03 20060202 Extend handling of GNU C -$! 0.04 20090402 Compaq -> hp -$CHECK_COMPILER: -$ if (.not. (its_decc .or. its_vaxc .or. its_gnuc)) -$ then -$ its_decc = (f$search("SYS$SYSTEM:DECC$COMPILER.EXE") .nes. "") -$ its_vaxc = .not. its_decc .and. (F$Search("SYS$System:VAXC.Exe") .nes. "") -$ its_gnuc = .not. (its_decc .or. its_vaxc) .and. (f$trnlnm("gnu_cc") .nes. "") -$ endif -$! -$! Exit if no compiler available -$! -$ if (.not. (its_decc .or. its_vaxc .or. its_gnuc)) -$ then goto CC_ERR -$ else -$ if its_decc -$ then -$ write sys$output "CC compiler check ... hp C" -$ if f$trnlnm("decc$no_rooted_search_lists") .nes. "" -$ then -$ dnrsl = f$trnlnm("decc$no_rooted_search_lists") -$ endif -$ define/nolog decc$no_rooted_search_lists 1 -$ else -$ if its_vaxc then write sys$output "CC compiler check ... VAX C" -$ if its_gnuc -$ then -$ write sys$output "CC compiler check ... GNU C" -$ if f$trnlnm(topt) then write topt "gnu_cc:[000000]gcclib.olb/lib" -$ if f$trnlnm(optf) then write optf "gnu_cc:[000000]gcclib.olb/lib" -$ cc = "gcc" -$ endif -$ if f$trnlnm(topt) then write topt "sys$share:vaxcrtl.exe/share" -$ if f$trnlnm(optf) then write optf "sys$share:vaxcrtl.exe/share" -$ endif -$ endif -$ return -$!------------------------------------------------------------------------------ -$! -$! If MMS/MMK are available dump out the descrip.mms if required -$! -$CREA_MMS: -$ write sys$output "Creating descrip.mms..." -$ create descrip.mms -$ open/append out descrip.mms -$ copy sys$input: out -$ deck -# descrip.mms: MMS description file for building zlib on VMS -# written by Martin P.J. Zinser -# - -OBJS = adler32.obj, compress.obj, crc32.obj, gzclose.obj, gzlib.obj\ - gzread.obj, gzwrite.obj, uncompr.obj, infback.obj\ - deflate.obj, trees.obj, zutil.obj, inflate.obj, \ - inftrees.obj, inffast.obj - -$ eod -$ write out "CFLAGS=", ccopt -$ write out "LOPTS=", lopts -$ write out "all : example.exe minigzip.exe libz.olb" -$ copy sys$input: out -$ deck - @ write sys$output " Example applications available" - -libz.olb : libz.olb($(OBJS)) - @ write sys$output " libz available" - -example.exe : example.obj libz.olb - link $(LOPTS) example,libz.olb/lib - -minigzip.exe : minigzip.obj libz.olb - link $(LOPTS) minigzip,libz.olb/lib - -clean : - delete *.obj;*,libz.olb;*,*.opt;*,*.exe;* - - -# Other dependencies. -adler32.obj : adler32.c zutil.h zlib.h zconf.h -compress.obj : compress.c zlib.h zconf.h -crc32.obj : crc32.c zutil.h zlib.h zconf.h -deflate.obj : deflate.c deflate.h zutil.h zlib.h zconf.h -example.obj : [.test]example.c zlib.h zconf.h -gzclose.obj : gzclose.c zutil.h zlib.h zconf.h -gzlib.obj : gzlib.c zutil.h zlib.h zconf.h -gzread.obj : gzread.c zutil.h zlib.h zconf.h -gzwrite.obj : gzwrite.c zutil.h zlib.h zconf.h -inffast.obj : inffast.c zutil.h zlib.h zconf.h inftrees.h inffast.h -inflate.obj : inflate.c zutil.h zlib.h zconf.h -inftrees.obj : inftrees.c zutil.h zlib.h zconf.h inftrees.h -minigzip.obj : [.test]minigzip.c zlib.h zconf.h -trees.obj : trees.c deflate.h zutil.h zlib.h zconf.h -uncompr.obj : uncompr.c zlib.h zconf.h -zutil.obj : zutil.c zutil.h zlib.h zconf.h -infback.obj : infback.c zutil.h inftrees.h inflate.h inffast.h inffixed.h -$ eod -$ close out -$ return -$!------------------------------------------------------------------------------ -$! -$! Read list of core library sources from makefile.in and create options -$! needed to build shareable image -$! -$CREA_OLIST: -$ open/read min makefile.in -$ open/write mod modules.opt -$ src_check_list = "OBJZ =#OBJG =" -$MRLOOP: -$ read/end=mrdone min rec -$ i = 0 -$SRC_CHECK_LOOP: -$ src_check = f$element(i, "#", src_check_list) -$ i = i+1 -$ if src_check .eqs. "#" then goto mrloop -$ if (f$extract(0,6,rec) .nes. src_check) then goto src_check_loop -$ rec = rec - src_check -$ gosub extra_filnam -$ if (f$element(1,"\",rec) .eqs. "\") then goto mrloop -$MRSLOOP: -$ read/end=mrdone min rec -$ gosub extra_filnam -$ if (f$element(1,"\",rec) .nes. "\") then goto mrsloop -$MRDONE: -$ close min -$ close mod -$ return -$!------------------------------------------------------------------------------ -$! -$! Take record extracted in crea_olist and split it into single filenames -$! -$EXTRA_FILNAM: -$ myrec = f$edit(rec - "\", "trim,compress") -$ i = 0 -$FELOOP: -$ srcfil = f$element(i," ", myrec) -$ if (srcfil .nes. " ") -$ then -$ write mod f$parse(srcfil,,,"NAME"), ".obj" -$ i = i + 1 -$ goto feloop -$ endif -$ return -$!------------------------------------------------------------------------------ -$! -$! Find current Zlib version number -$! -$FIND_VERSION: -$ open/read h_in 'v_file' -$hloop: -$ read/end=hdone h_in rec -$ rec = f$edit(rec,"TRIM") -$ if (f$extract(0,1,rec) .nes. "#") then goto hloop -$ rec = f$edit(rec - "#", "TRIM") -$ if f$element(0," ",rec) .nes. "define" then goto hloop -$ if f$element(1," ",rec) .eqs. v_string -$ then -$ version = 'f$element(2," ",rec)' -$ goto hdone -$ endif -$ goto hloop -$hdone: -$ close h_in -$ return -$!------------------------------------------------------------------------------ -$! -$CHECK_CONFIG: -$! -$ in_ldef = f$locate(cdef,libdefs) -$ if (in_ldef .lt. f$length(libdefs)) -$ then -$ write aconf "#define ''cdef' 1" -$ libdefs = f$extract(0,in_ldef,libdefs) + - - f$extract(in_ldef + f$length(cdef) + 1, - - f$length(libdefs) - in_ldef - f$length(cdef) - 1, - - libdefs) -$ else -$ if (f$type('cdef') .eqs. "INTEGER") -$ then -$ write aconf "#define ''cdef' ", 'cdef' -$ else -$ if (f$type('cdef') .eqs. "STRING") -$ then -$ write aconf "#define ''cdef' ", """", '''cdef'', """" -$ else -$ gosub check_cc_def -$ endif -$ endif -$ endif -$ return -$!------------------------------------------------------------------------------ -$! -$! Check if this is a define relating to the properties of the C/C++ -$! compiler -$! -$ CHECK_CC_DEF: -$ if (cdef .eqs. "_LARGEFILE64_SOURCE") -$ then -$ copy sys$input: 'tc' -$ deck -#include "tconfig" -#define _LARGEFILE -#include - -int main(){ -FILE *fp; - fp = fopen("temp.txt","r"); - fseeko(fp,1,SEEK_SET); - fclose(fp); -} - -$ eod -$ test_inv = false -$ comm_h = false -$ gosub cc_prop_check -$ return -$ endif -$ write aconf "/* ", line, " */" -$ return -$!------------------------------------------------------------------------------ -$! -$! Check for properties of C/C++ compiler -$! -$! Version history -$! 0.01 20031020 First version to receive a number -$! 0.02 20031022 Added logic for defines with value -$! 0.03 20040309 Make sure local config file gets not deleted -$! 0.04 20041230 Also write include for configure run -$! 0.05 20050103 Add processing of "comment defines" -$CC_PROP_CHECK: -$ cc_prop = true -$ is_need = false -$ is_need = (f$extract(0,4,cdef) .eqs. "NEED") .or. (test_inv .eq. true) -$ if f$search(th) .eqs. "" then create 'th' -$ set message/nofac/noident/nosever/notext -$ on error then continue -$ cc 'tmpnam' -$ if .not. ($status) then cc_prop = false -$ on error then continue -$! The headers might lie about the capabilities of the RTL -$ link 'tmpnam',tmp.opt/opt -$ if .not. ($status) then cc_prop = false -$ set message/fac/ident/sever/text -$ on error then goto err_exit -$ delete/nolog 'tmpnam'.*;*/exclude='th' -$ if (cc_prop .and. .not. is_need) .or. - - (.not. cc_prop .and. is_need) -$ then -$ write sys$output "Checking for ''cdef'... yes" -$ if f$type('cdef_val'_yes) .nes. "" -$ then -$ if f$type('cdef_val'_yes) .eqs. "INTEGER" - - then call write_config f$fao("#define !AS !UL",cdef,'cdef_val'_yes) -$ if f$type('cdef_val'_yes) .eqs. "STRING" - - then call write_config f$fao("#define !AS !AS",cdef,'cdef_val'_yes) -$ else -$ call write_config f$fao("#define !AS 1",cdef) -$ endif -$ if (cdef .eqs. "HAVE_FSEEKO") .or. (cdef .eqs. "_LARGE_FILES") .or. - - (cdef .eqs. "_LARGEFILE64_SOURCE") then - - call write_config f$string("#define _LARGEFILE 1") -$ else -$ write sys$output "Checking for ''cdef'... no" -$ if (comm_h) -$ then - call write_config f$fao("/* !AS */",line) -$ else -$ if f$type('cdef_val'_no) .nes. "" -$ then -$ if f$type('cdef_val'_no) .eqs. "INTEGER" - - then call write_config f$fao("#define !AS !UL",cdef,'cdef_val'_no) -$ if f$type('cdef_val'_no) .eqs. "STRING" - - then call write_config f$fao("#define !AS !AS",cdef,'cdef_val'_no) -$ else -$ call write_config f$fao("#undef !AS",cdef) -$ endif -$ endif -$ endif -$ return -$!------------------------------------------------------------------------------ -$! -$! Check for properties of C/C++ compiler with multiple result values -$! -$! Version history -$! 0.01 20040127 First version -$! 0.02 20050103 Reconcile changes from cc_prop up to version 0.05 -$CC_MPROP_CHECK: -$ cc_prop = true -$ i = 1 -$ idel = 1 -$ MT_LOOP: -$ if f$type(result_'i') .eqs. "STRING" -$ then -$ set message/nofac/noident/nosever/notext -$ on error then continue -$ cc 'tmpnam'_'i' -$ if .not. ($status) then cc_prop = false -$ on error then continue -$! The headers might lie about the capabilities of the RTL -$ link 'tmpnam'_'i',tmp.opt/opt -$ if .not. ($status) then cc_prop = false -$ set message/fac/ident/sever/text -$ on error then goto err_exit -$ delete/nolog 'tmpnam'_'i'.*;* -$ if (cc_prop) -$ then -$ write sys$output "Checking for ''cdef'... ", mdef_'i' -$ if f$type(mdef_'i') .eqs. "INTEGER" - - then call write_config f$fao("#define !AS !UL",cdef,mdef_'i') -$ if f$type('cdef_val'_yes) .eqs. "STRING" - - then call write_config f$fao("#define !AS !AS",cdef,mdef_'i') -$ goto msym_clean -$ else -$ i = i + 1 -$ goto mt_loop -$ endif -$ endif -$ write sys$output "Checking for ''cdef'... no" -$ call write_config f$fao("#undef !AS",cdef) -$ MSYM_CLEAN: -$ if (idel .le. msym_max) -$ then -$ delete/sym mdef_'idel' -$ idel = idel + 1 -$ goto msym_clean -$ endif -$ return -$!------------------------------------------------------------------------------ -$! -$! Write configuration to both permanent and temporary config file -$! -$! Version history -$! 0.01 20031029 First version to receive a number -$! -$WRITE_CONFIG: SUBROUTINE -$ write aconf 'p1' -$ open/append confh 'th' -$ write confh 'p1' -$ close confh -$ENDSUBROUTINE -$!------------------------------------------------------------------------------ -$! -$! Analyze the project map file and create the symbol vector for a shareable -$! image from it -$! -$! Version history -$! 0.01 20120128 First version -$! 0.02 20120226 Add pre-load logic -$! -$ MAP_2_SHOPT: Subroutine -$! -$ SAY := "WRITE_ SYS$OUTPUT" -$! -$ IF F$SEARCH("''P1'") .EQS. "" -$ THEN -$ SAY "MAP_2_SHOPT-E-NOSUCHFILE: Error, inputfile ''p1' not available" -$ goto exit_m2s -$ ENDIF -$ IF "''P2'" .EQS. "" -$ THEN -$ SAY "MAP_2_SHOPT: Error, no output file provided" -$ goto exit_m2s -$ ENDIF -$! -$ module1 = "deflate#deflateEnd#deflateInit_#deflateParams#deflateSetDictionary" -$ module2 = "gzclose#gzerror#gzgetc#gzgets#gzopen#gzprintf#gzputc#gzputs#gzread" -$ module3 = "gzseek#gztell#inflate#inflateEnd#inflateInit_#inflateSetDictionary" -$ module4 = "inflateSync#uncompress#zlibVersion#compress" -$ open/read map 'p1 -$ if axp .or. ia64 -$ then -$ open/write aopt a.opt -$ open/write bopt b.opt -$ write aopt " CASE_SENSITIVE=YES" -$ write bopt "SYMBOL_VECTOR= (-" -$ mod_sym_num = 1 -$ MOD_SYM_LOOP: -$ if f$type(module'mod_sym_num') .nes. "" -$ then -$ mod_in = 0 -$ MOD_SYM_IN: -$ shared_proc = f$element(mod_in, "#", module'mod_sym_num') -$ if shared_proc .nes. "#" -$ then -$ write aopt f$fao(" symbol_vector=(!AS/!AS=PROCEDURE)",- - f$edit(shared_proc,"upcase"),shared_proc) -$ write bopt f$fao("!AS=PROCEDURE,-",shared_proc) -$ mod_in = mod_in + 1 -$ goto mod_sym_in -$ endif -$ mod_sym_num = mod_sym_num + 1 -$ goto mod_sym_loop -$ endif -$MAP_LOOP: -$ read/end=map_end map line -$ if (f$locate("{",line).lt. f$length(line)) .or. - - (f$locate("global:", line) .lt. f$length(line)) -$ then -$ proc = true -$ goto map_loop -$ endif -$ if f$locate("}",line).lt. f$length(line) then proc = false -$ if f$locate("local:", line) .lt. f$length(line) then proc = false -$ if proc -$ then -$ shared_proc = f$edit(line,"collapse") -$ chop_semi = f$locate(";", shared_proc) -$ if chop_semi .lt. f$length(shared_proc) then - - shared_proc = f$extract(0, chop_semi, shared_proc) -$ write aopt f$fao(" symbol_vector=(!AS/!AS=PROCEDURE)",- - f$edit(shared_proc,"upcase"),shared_proc) -$ write bopt f$fao("!AS=PROCEDURE,-",shared_proc) -$ endif -$ goto map_loop -$MAP_END: -$ close/nolog aopt -$ close/nolog bopt -$ open/append libopt 'p2' -$ open/read aopt a.opt -$ open/read bopt b.opt -$ALOOP: -$ read/end=aloop_end aopt line -$ write libopt line -$ goto aloop -$ALOOP_END: -$ close/nolog aopt -$ sv = "" -$BLOOP: -$ read/end=bloop_end bopt svn -$ if (svn.nes."") -$ then -$ if (sv.nes."") then write libopt sv -$ sv = svn -$ endif -$ goto bloop -$BLOOP_END: -$ write libopt f$extract(0,f$length(sv)-2,sv), "-" -$ write libopt ")" -$ close/nolog bopt -$ delete/nolog/noconf a.opt;*,b.opt;* -$ else -$ if vax -$ then -$ open/append libopt 'p2' -$ mod_sym_num = 1 -$ VMOD_SYM_LOOP: -$ if f$type(module'mod_sym_num') .nes. "" -$ then -$ mod_in = 0 -$ VMOD_SYM_IN: -$ shared_proc = f$element(mod_in, "#", module'mod_sym_num') -$ if shared_proc .nes. "#" -$ then -$ write libopt f$fao("UNIVERSAL=!AS",- - f$edit(shared_proc,"upcase")) -$ mod_in = mod_in + 1 -$ goto vmod_sym_in -$ endif -$ mod_sym_num = mod_sym_num + 1 -$ goto vmod_sym_loop -$ endif -$VMAP_LOOP: -$ read/end=vmap_end map line -$ if (f$locate("{",line).lt. f$length(line)) .or. - - (f$locate("global:", line) .lt. f$length(line)) -$ then -$ proc = true -$ goto vmap_loop -$ endif -$ if f$locate("}",line).lt. f$length(line) then proc = false -$ if f$locate("local:", line) .lt. f$length(line) then proc = false -$ if proc -$ then -$ shared_proc = f$edit(line,"collapse") -$ chop_semi = f$locate(";", shared_proc) -$ if chop_semi .lt. f$length(shared_proc) then - - shared_proc = f$extract(0, chop_semi, shared_proc) -$ write libopt f$fao("UNIVERSAL=!AS",- - f$edit(shared_proc,"upcase")) -$ endif -$ goto vmap_loop -$VMAP_END: -$ else -$ write sys$output "Unknown Architecture (Not VAX, AXP, or IA64)" -$ write sys$output "No options file created" -$ endif -$ endif -$ EXIT_M2S: -$ close/nolog map -$ close/nolog libopt -$ endsubroutine diff --git a/contrib/zlib/treebuild.xml b/contrib/zlib/treebuild.xml deleted file mode 100644 index 6bd6677c4..000000000 --- a/contrib/zlib/treebuild.xml +++ /dev/null @@ -1,116 +0,0 @@ - - - - zip compression library - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/contrib/zlib/zlib.3 b/contrib/zlib/zlib.3 deleted file mode 100644 index fac206b52..000000000 --- a/contrib/zlib/zlib.3 +++ /dev/null @@ -1,149 +0,0 @@ -.TH ZLIB 3 "xx Jan 2017" -.SH NAME -zlib \- compression/decompression library -.SH SYNOPSIS -[see -.I zlib.h -for full description] -.SH DESCRIPTION -The -.I zlib -library is a general purpose data compression library. -The code is thread safe, assuming that the standard library functions -used are thread safe, such as memory allocation routines. -It provides in-memory compression and decompression functions, -including integrity checks of the uncompressed data. -This version of the library supports only one compression method (deflation) -but other algorithms may be added later -with the same stream interface. -.LP -Compression can be done in a single step if the buffers are large enough -or can be done by repeated calls of the compression function. -In the latter case, -the application must provide more input and/or consume the output -(providing more output space) before each call. -.LP -The library also supports reading and writing files in -.IR gzip (1) -(.gz) format -with an interface similar to that of stdio. -.LP -The library does not install any signal handler. -The decoder checks the consistency of the compressed data, -so the library should never crash even in the case of corrupted input. -.LP -All functions of the compression library are documented in the file -.IR zlib.h . -The distribution source includes examples of use of the library -in the files -.I test/example.c -and -.IR test/minigzip.c, -as well as other examples in the -.IR examples/ -directory. -.LP -Changes to this version are documented in the file -.I ChangeLog -that accompanies the source. -.LP -.I zlib -is built in to many languages and operating systems, including but not limited to -Java, Python, .NET, PHP, Perl, Ruby, Swift, and Go. -.LP -An experimental package to read and write files in the .zip format, -written on top of -.I zlib -by Gilles Vollant (info@winimage.com), -is available at: -.IP -http://www.winimage.com/zLibDll/minizip.html -and also in the -.I contrib/minizip -directory of the main -.I zlib -source distribution. -.SH "SEE ALSO" -The -.I zlib -web site can be found at: -.IP -http://zlib.net/ -.LP -The data format used by the -.I zlib -library is described by RFC -(Request for Comments) 1950 to 1952 in the files: -.IP -http://tools.ietf.org/html/rfc1950 (for the zlib header and trailer format) -.br -http://tools.ietf.org/html/rfc1951 (for the deflate compressed data format) -.br -http://tools.ietf.org/html/rfc1952 (for the gzip header and trailer format) -.LP -Mark Nelson wrote an article about -.I zlib -for the Jan. 1997 issue of Dr. Dobb's Journal; -a copy of the article is available at: -.IP -http://marknelson.us/1997/01/01/zlib-engine/ -.SH "REPORTING PROBLEMS" -Before reporting a problem, -please check the -.I zlib -web site to verify that you have the latest version of -.IR zlib ; -otherwise, -obtain the latest version and see if the problem still exists. -Please read the -.I zlib -FAQ at: -.IP -http://zlib.net/zlib_faq.html -.LP -before asking for help. -Send questions and/or comments to zlib@gzip.org, -or (for the Windows DLL version) to Gilles Vollant (info@winimage.com). -.SH AUTHORS AND LICENSE -Version 1.2.11.1 -.LP -Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler -.LP -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. -.LP -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: -.LP -.nr step 1 1 -.IP \n[step]. 3 -The origin of this software must not be misrepresented; you must not -claim that you wrote the original software. If you use this software -in a product, an acknowledgment in the product documentation would be -appreciated but is not required. -.IP \n+[step]. -Altered source versions must be plainly marked as such, and must not be -misrepresented as being the original software. -.IP \n+[step]. -This notice may not be removed or altered from any source distribution. -.LP -Jean-loup Gailly Mark Adler -.br -jloup@gzip.org madler@alumni.caltech.edu -.LP -The deflate format used by -.I zlib -was defined by Phil Katz. -The deflate and -.I zlib -specifications were written by L. Peter Deutsch. -Thanks to all the people who reported problems and suggested various -improvements in -.IR zlib ; -who are too numerous to cite here. -.LP -UNIX manual page by R. P. C. Rodgers, -U.S. National Library of Medicine (rodgers@nlm.nih.gov). -.\" end of man page diff --git a/contrib/zlib/zlib.3.pdf b/contrib/zlib/zlib.3.pdf deleted file mode 100644 index 48e6147385dc9dfc0058474f2e1efa1453409e8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19320 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μ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 TS4a>yCp#x9HMN+$IO_icwA6bT diff --git a/contrib/zlib/zlib.map b/contrib/zlib/zlib.map deleted file mode 100644 index 82ce98cf7..000000000 --- a/contrib/zlib/zlib.map +++ /dev/null @@ -1,94 +0,0 @@ -ZLIB_1.2.0 { - global: - compressBound; - deflateBound; - inflateBack; - inflateBackEnd; - inflateBackInit_; - inflateCopy; - local: - deflate_copyright; - inflate_copyright; - inflate_fast; - inflate_table; - zcalloc; - zcfree; - z_errmsg; - gz_error; - gz_intmax; - _*; -}; - -ZLIB_1.2.0.2 { - gzclearerr; - gzungetc; - zlibCompileFlags; -} ZLIB_1.2.0; - -ZLIB_1.2.0.8 { - deflatePrime; -} ZLIB_1.2.0.2; - -ZLIB_1.2.2 { - adler32_combine; - crc32_combine; - deflateSetHeader; - inflateGetHeader; -} ZLIB_1.2.0.8; - -ZLIB_1.2.2.3 { - deflateTune; - gzdirect; -} ZLIB_1.2.2; - -ZLIB_1.2.2.4 { - inflatePrime; -} ZLIB_1.2.2.3; - -ZLIB_1.2.3.3 { - adler32_combine64; - crc32_combine64; - gzopen64; - gzseek64; - gztell64; - inflateUndermine; -} ZLIB_1.2.2.4; - -ZLIB_1.2.3.4 { - inflateReset2; - inflateMark; -} ZLIB_1.2.3.3; - -ZLIB_1.2.3.5 { - gzbuffer; - gzoffset; - gzoffset64; - gzclose_r; - gzclose_w; -} ZLIB_1.2.3.4; - -ZLIB_1.2.5.1 { - deflatePending; -} ZLIB_1.2.3.5; - -ZLIB_1.2.5.2 { - deflateResetKeep; - gzgetc_; - inflateResetKeep; -} ZLIB_1.2.5.1; - -ZLIB_1.2.7.1 { - inflateGetDictionary; - gzvprintf; -} ZLIB_1.2.5.2; - -ZLIB_1.2.9 { - inflateCodesUsed; - inflateValidate; - uncompress2; - gzfread; - gzfwrite; - deflateGetDictionary; - adler32_z; - crc32_z; -} ZLIB_1.2.7.1; diff --git a/contrib/zlib/zlib.pc.in b/contrib/zlib/zlib.pc.in deleted file mode 100644 index 7e5acf9c7..000000000 --- a/contrib/zlib/zlib.pc.in +++ /dev/null @@ -1,13 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -sharedlibdir=@sharedlibdir@ -includedir=@includedir@ - -Name: zlib -Description: zlib compression library -Version: @VERSION@ - -Requires: -Libs: -L${libdir} -L${sharedlibdir} -lz -Cflags: -I${includedir} diff --git a/contrib/zlib/zlib2ansi b/contrib/zlib/zlib2ansi deleted file mode 100644 index 15e3e165f..000000000 --- a/contrib/zlib/zlib2ansi +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/perl - -# Transform K&R C function definitions into ANSI equivalent. -# -# Author: Paul Marquess -# Version: 1.0 -# Date: 3 October 2006 - -# TODO -# -# Asumes no function pointer parameters. unless they are typedefed. -# Assumes no literal strings that look like function definitions -# Assumes functions start at the beginning of a line - -use strict; -use warnings; - -local $/; -$_ = <>; - -my $sp = qr{ \s* (?: /\* .*? \*/ )? \s* }x; # assume no nested comments - -my $d1 = qr{ $sp (?: [\w\*\s]+ $sp)* $sp \w+ $sp [\[\]\s]* $sp }x ; -my $decl = qr{ $sp (?: \w+ $sp )+ $d1 }xo ; -my $dList = qr{ $sp $decl (?: $sp , $d1 )* $sp ; $sp }xo ; - - -while (s/^ - ( # Start $1 - ( # Start $2 - .*? # Minimal eat content - ( ^ \w [\w\s\*]+ ) # $3 -- function name - \s* # optional whitespace - ) # $2 - Matched up to before parameter list - - \( \s* # Literal "(" + optional whitespace - ( [^\)]+ ) # $4 - one or more anythings except ")" - \s* \) # optional whitespace surrounding a Literal ")" - - ( (?: $dList )+ ) # $5 - - $sp ^ { # literal "{" at start of line - ) # Remember to $1 - //xsom - ) -{ - my $all = $1 ; - my $prefix = $2; - my $param_list = $4 ; - my $params = $5; - - StripComments($params); - StripComments($param_list); - $param_list =~ s/^\s+//; - $param_list =~ s/\s+$//; - - my $i = 0 ; - my %pList = map { $_ => $i++ } - split /\s*,\s*/, $param_list; - my $pMatch = '(\b' . join('|', keys %pList) . '\b)\W*$' ; - - my @params = split /\s*;\s*/, $params; - my @outParams = (); - foreach my $p (@params) - { - if ($p =~ /,/) - { - my @bits = split /\s*,\s*/, $p; - my $first = shift @bits; - $first =~ s/^\s*//; - push @outParams, $first; - $first =~ /^(\w+\s*)/; - my $type = $1 ; - push @outParams, map { $type . $_ } @bits; - } - else - { - $p =~ s/^\s+//; - push @outParams, $p; - } - } - - - my %tmp = map { /$pMatch/; $_ => $pList{$1} } - @outParams ; - - @outParams = map { " $_" } - sort { $tmp{$a} <=> $tmp{$b} } - @outParams ; - - print $prefix ; - print "(\n" . join(",\n", @outParams) . ")\n"; - print "{" ; - -} - -# Output any trailing code. -print ; -exit 0; - - -sub StripComments -{ - - no warnings; - - # Strip C & C++ coments - # From the perlfaq - $_[0] =~ - - s{ - /\* ## Start of /* ... */ comment - [^*]*\*+ ## Non-* followed by 1-or-more *'s - ( - [^/*][^*]*\*+ - )* ## 0-or-more things which don't start with / - ## but do end with '*' - / ## End of /* ... */ comment - - | ## OR C++ Comment - // ## Start of C++ comment // - [^\n]* ## followed by 0-or-more non end of line characters - - | ## OR various things which aren't comments: - - ( - " ## Start of " ... " string - ( - \\. ## Escaped char - | ## OR - [^"\\] ## Non "\ - )* - " ## End of " ... " string - - | ## OR - - ' ## Start of ' ... ' string - ( - \\. ## Escaped char - | ## OR - [^'\\] ## Non '\ - )* - ' ## End of ' ... ' string - - | ## OR - - . ## Anything other char - [^/"'\\]* ## Chars which doesn't start a comment, string or escape - ) - }{$2}gxs; - -} From bf1aaf98f9d032a4c13d55d08e792c9d7cee3b97 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 10:51:48 +0300 Subject: [PATCH 153/490] IFCImporter: Add explicit instantiation of log_prefix so IFCMaterial.cpp can see it --- code/IFCLoader.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/code/IFCLoader.h b/code/IFCLoader.h index 4cf116f8e..000683bb8 100644 --- a/code/IFCLoader.h +++ b/code/IFCLoader.h @@ -60,6 +60,13 @@ namespace Assimp { class DB; } +#ifndef _MSC_VER + // GCC and Clang need to see this explicit declaration to avoid warning + // MSVC complains about redeclaration even though this is just the + // declaration, not the definition + class IFCImporter; + template<> const std::string LogFunctions::log_prefix; +#endif // _MSC_VER // ------------------------------------------------------------------------------------------- /** Load the IFC format, which is an open specification to describe building and construction From afce9842286a441d5a93a4920f6ce9ca9d3f488c Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 11:05:27 +0300 Subject: [PATCH 154/490] FBXImporter: Add explicit instantiation of log_prefix so other FBX source files can see it --- code/FBXImporter.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/code/FBXImporter.h b/code/FBXImporter.h index 43be97ffa..54be8384e 100644 --- a/code/FBXImporter.h +++ b/code/FBXImporter.h @@ -58,6 +58,14 @@ namespace Formatter { typedef class basic_formatter< char, std::char_traits, std::allocator > format; } +#ifndef _MSC_VER +// GCC and Clang need to see this explicit declaration to avoid warning +// MSVC complains about redeclaration even though this is just the +// declaration, not the definition +class FBXImporter; +template<> const std::string LogFunctions::log_prefix; +#endif // _MSC_VER + // ------------------------------------------------------------------------------------------- /** Load the Autodesk FBX file format. From 65547d57606d9c9345b00d38271f3e499cee4001 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 11:10:22 +0300 Subject: [PATCH 155/490] Upgrade RapidJSON to get rid of a clang warning --- .../rapidjson/include/rapidjson/allocators.h | 42 +- .../rapidjson/include/rapidjson/document.h | 1073 +++++++-- .../include/rapidjson/encodedstream.h | 58 +- .../rapidjson/include/rapidjson/encodings.h | 233 +- .../rapidjson/include/rapidjson/error/en.h | 21 +- .../rapidjson/include/rapidjson/error/error.h | 27 +- .../include/rapidjson/filereadstream.h | 13 +- .../include/rapidjson/filewritestream.h | 17 +- contrib/rapidjson/include/rapidjson/fwd.h | 151 ++ .../include/rapidjson/internal/biginteger.h | 14 +- .../include/rapidjson/internal/diyfp.h | 19 +- .../include/rapidjson/internal/dtoa.h | 50 +- .../include/rapidjson/internal/ieee754.h | 3 +- .../include/rapidjson/internal/regex.h | 734 ++++++ .../include/rapidjson/internal/stack.h | 68 +- .../include/rapidjson/internal/strfunc.h | 32 +- .../include/rapidjson/internal/strtod.h | 39 +- .../include/rapidjson/internal/swap.h | 9 + .../include/rapidjson/istreamwrapper.h | 115 + .../include/rapidjson/memorybuffer.h | 2 +- .../include/rapidjson/memorystream.h | 16 +- .../include/rapidjson/msinttypes/stdint.h | 8 +- .../include/rapidjson/ostreamwrapper.h | 81 + contrib/rapidjson/include/rapidjson/pointer.h | 131 +- .../include/rapidjson/prettywriter.h | 98 +- .../rapidjson/include/rapidjson/rapidjson.h | 314 ++- contrib/rapidjson/include/rapidjson/reader.h | 1345 ++++++++--- contrib/rapidjson/include/rapidjson/schema.h | 2016 +++++++++++++++++ contrib/rapidjson/include/rapidjson/stream.h | 179 ++ .../include/rapidjson/stringbuffer.h | 30 +- contrib/rapidjson/include/rapidjson/writer.h | 468 +++- contrib/rapidjson/license.txt | 2 +- contrib/rapidjson/readme.md | 81 +- 33 files changed, 6467 insertions(+), 1022 deletions(-) create mode 100644 contrib/rapidjson/include/rapidjson/fwd.h create mode 100644 contrib/rapidjson/include/rapidjson/internal/regex.h create mode 100644 contrib/rapidjson/include/rapidjson/istreamwrapper.h create mode 100644 contrib/rapidjson/include/rapidjson/ostreamwrapper.h create mode 100644 contrib/rapidjson/include/rapidjson/schema.h create mode 100644 contrib/rapidjson/include/rapidjson/stream.h diff --git a/contrib/rapidjson/include/rapidjson/allocators.h b/contrib/rapidjson/include/rapidjson/allocators.h index d74a67155..655f4a385 100644 --- a/contrib/rapidjson/include/rapidjson/allocators.h +++ b/contrib/rapidjson/include/rapidjson/allocators.h @@ -179,9 +179,10 @@ public: size = RAPIDJSON_ALIGN(size); if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) - AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size); + if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) + return NULL; - void *buffer = reinterpret_cast(chunkHead_ + 1) + chunkHead_->size; + void *buffer = reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size; chunkHead_->size += size; return buffer; } @@ -194,14 +195,16 @@ public: if (newSize == 0) return NULL; + originalSize = RAPIDJSON_ALIGN(originalSize); + newSize = RAPIDJSON_ALIGN(newSize); + // Do not shrink if new size is smaller than original if (originalSize >= newSize) return originalPtr; // Simply expand it if it is the last allocation and there is sufficient space - if (originalPtr == (char *)(chunkHead_ + 1) + chunkHead_->size - originalSize) { + if (originalPtr == reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) { size_t increment = static_cast(newSize - originalSize); - increment = RAPIDJSON_ALIGN(increment); if (chunkHead_->size + increment <= chunkHead_->capacity) { chunkHead_->size += increment; return originalPtr; @@ -209,11 +212,13 @@ public: } // Realloc process: allocate and copy memory, do not free original buffer. - void* newBuffer = Malloc(newSize); - RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly. - if (originalSize) - std::memcpy(newBuffer, originalPtr, originalSize); - return newBuffer; + if (void* newBuffer = Malloc(newSize)) { + if (originalSize) + std::memcpy(newBuffer, originalPtr, originalSize); + return newBuffer; + } + else + return NULL; } //! Frees a memory block (concept Allocator) @@ -227,15 +232,20 @@ private: //! Creates a new chunk. /*! \param capacity Capacity of the chunk in bytes. + \return true if success. */ - void AddChunk(size_t capacity) { + bool AddChunk(size_t capacity) { if (!baseAllocator_) - ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator()); - ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(sizeof(ChunkHeader) + capacity)); - chunk->capacity = capacity; - chunk->size = 0; - chunk->next = chunkHead_; - chunkHead_ = chunk; + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)(); + if (ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) { + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = chunkHead_; + chunkHead_ = chunk; + return true; + } + else + return false; } static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity. diff --git a/contrib/rapidjson/include/rapidjson/document.h b/contrib/rapidjson/include/rapidjson/document.h index c6acbd907..93b091f64 100644 --- a/contrib/rapidjson/include/rapidjson/document.h +++ b/contrib/rapidjson/include/rapidjson/document.h @@ -20,40 +20,29 @@ #include "reader.h" #include "internal/meta.h" #include "internal/strfunc.h" +#include "memorystream.h" +#include "encodedstream.h" #include // placement new +#include +RAPIDJSON_DIAG_PUSH #ifdef _MSC_VER -RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant -#elif defined(__GNUC__) -RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +#ifdef __GNUC__ RAPIDJSON_DIAG_OFF(effc++) +#if __GNUC__ >= 6 +RAPIDJSON_DIAG_OFF(terminate) // ignore throwing RAPIDJSON_ASSERT in RAPIDJSON_NOEXCEPT functions #endif - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_HAS_STDSTRING - -#ifndef RAPIDJSON_HAS_STDSTRING -#ifdef RAPIDJSON_DOXYGEN_RUNNING -#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation -#else -#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default -#endif -/*! \def RAPIDJSON_HAS_STDSTRING - \ingroup RAPIDJSON_CONFIG - \brief Enable RapidJSON support for \c std::string - - By defining this preprocessor symbol to \c 1, several convenience functions for using - \ref rapidjson::GenericValue with \c std::string are enabled, especially - for construction and comparison. - - \hideinitializer -*/ -#endif // !defined(RAPIDJSON_HAS_STDSTRING) - -#if RAPIDJSON_HAS_STDSTRING -#include -#endif // RAPIDJSON_HAS_STDSTRING +#endif // __GNUC__ #ifndef RAPIDJSON_NOMEMBERITERATORCLASS #include // std::iterator, std::random_access_iterator_tag @@ -69,6 +58,9 @@ RAPIDJSON_NAMESPACE_BEGIN template class GenericValue; +template +class GenericDocument; + //! Name-value pair in a JSON object value. /*! This class was internal to GenericValue. It used to be a inner struct. @@ -155,6 +147,7 @@ public: Otherwise, the copy constructor is implicitly defined. */ GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} + Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; } //! @name stepping //@{ @@ -257,6 +250,7 @@ struct GenericStringRef { typedef CharType Ch; //!< character type of the string //! Create string reference from \c const character array +#ifndef __clang__ // -Wdocumentation /*! This constructor implicitly creates a constant string reference from a \c const character array. It has better performance than @@ -279,11 +273,13 @@ struct GenericStringRef { In such cases, the referenced string should be \b copied to the GenericValue instead. */ +#endif template GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT : s(str), length(N-1) {} //! Explicitly create string reference from \c const character pointer +#ifndef __clang__ // -Wdocumentation /*! This constructor can be used to \b explicitly create a reference to a constant string pointer. @@ -302,18 +298,23 @@ struct GenericStringRef { In such cases, the referenced string should be \b copied to the GenericValue instead. */ +#endif explicit GenericStringRef(const CharType* str) - : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != NULL); } + : s(str), length(NotNullStrLen(str)) {} //! Create constant string reference from pointer and length +#ifndef __clang__ // -Wdocumentation /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue \param len length of the string, excluding the trailing NULL terminator \post \ref s == str && \ref length == len \note Constant complexity. */ +#endif GenericStringRef(const CharType* str, SizeType len) - : s(str), length(len) { RAPIDJSON_ASSERT(s != NULL); } + : s(RAPIDJSON_LIKELY(str) ? str : emptyString), length(len) { RAPIDJSON_ASSERT(str != 0 || len == 0u); } + + GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} //! implicit conversion to plain CharType pointer operator const Ch *() const { return s; } @@ -322,13 +323,24 @@ struct GenericStringRef { const SizeType length; //!< length of the string (excluding the trailing NULL terminator) private: - //! Disallow copy-assignment - GenericStringRef operator=(const GenericStringRef&); + SizeType NotNullStrLen(const CharType* str) { + RAPIDJSON_ASSERT(str != 0); + return internal::StrLen(str); + } + + /// Empty string - used when passing in a NULL pointer + static const Ch emptyString[]; + //! Disallow construction from non-const array template GenericStringRef(CharType (&str)[N]) /* = delete */; + //! Copy assignment operator not permitted - immutable type + GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; }; +template +const CharType GenericStringRef::emptyString[] = { CharType() }; + //! Mark a character pointer as constant string /*! Mark a plain character pointer as a "string literal". This function can be used to avoid copying a character string to be referenced as a @@ -343,7 +355,7 @@ private: */ template inline GenericStringRef StringRef(const CharType* str) { - return GenericStringRef(str, internal::StrLen(str)); + return GenericStringRef(str); } //! Mark a character pointer as constant string @@ -401,6 +413,127 @@ template struct IsGenericValue : IsGenericValueImpl::Type {}; } // namespace internal +/////////////////////////////////////////////////////////////////////////////// +// TypeHelper + +namespace internal { + +template +struct TypeHelper {}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsBool(); } + static bool Get(const ValueType& v) { return v.GetBool(); } + static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); } + static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static int Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt64(); } + static int64_t Get(const ValueType& v) { return v.GetInt64(); } + static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); } + static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint64(); } + static uint64_t Get(const ValueType& v) { return v.GetUint64(); } + static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); } + static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsDouble(); } + static double Get(const ValueType& v) { return v.GetDouble(); } + static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); } + static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsFloat(); } + static float Get(const ValueType& v) { return v.GetFloat(); } + static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); } + static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); } +}; + +template +struct TypeHelper { + typedef const typename ValueType::Ch* StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return v.GetString(); } + static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); } + static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; + +#if RAPIDJSON_HAS_STDSTRING +template +struct TypeHelper > { + typedef std::basic_string StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return StringType(v.GetString(), v.GetStringLength()); } + static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; +#endif + +template +struct TypeHelper { + typedef typename ValueType::Array ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(ValueType& v) { return v.GetArray(); } + static ValueType& Set(ValueType& v, ArrayType data) { return v = data; } + static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstArray ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(const ValueType& v) { return v.GetArray(); } +}; + +template +struct TypeHelper { + typedef typename ValueType::Object ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(ValueType& v) { return v.GetObject(); } + static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } + static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstObject ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(const ValueType& v) { return v.GetObject(); } +}; + +} // namespace internal + +// Forward declarations +template class GenericArray; +template class GenericObject; + /////////////////////////////////////////////////////////////////////////////// // GenericValue @@ -428,17 +561,21 @@ public: typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. typedef GenericValue ValueType; //!< Value type of itself. + typedef GenericArray Array; + typedef GenericArray ConstArray; + typedef GenericObject Object; + typedef GenericObject ConstObject; //!@name Constructors and destructor. //@{ //! Default constructor creates a null value. - GenericValue() RAPIDJSON_NOEXCEPT : data_(), flags_(kNullFlag) {} + GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Move constructor in C++11 - GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_), flags_(rhs.flags_) { - rhs.flags_ = kNullFlag; // give up contents + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) { + rhs.data_.f.flags = kNullFlag; // give up contents } #endif @@ -446,6 +583,16 @@ private: //! Copy constructor is not permitted. GenericValue(const GenericValue& rhs); +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Moving from a GenericDocument is not permitted. + template + GenericValue(GenericDocument&& rhs); + + //! Move assignment from a GenericDocument is not permitted. + template + GenericValue& operator=(GenericDocument&& rhs); +#endif + public: //! Constructor with JSON value type. @@ -453,13 +600,13 @@ public: \param type Type of the value. \note Default content for number is zero. */ - explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_(), flags_() { - static const unsigned defaultFlags[7] = { + explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() { + static const uint16_t defaultFlags[7] = { kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, kNumberAnyFlag }; - RAPIDJSON_ASSERT(type <= kNumberType); - flags_ = defaultFlags[type]; + RAPIDJSON_ASSERT(type >= kNullType && type <= kNumberType); + data_.f.flags = defaultFlags[type]; // Use ShortString to store empty string. if (type == kStringType) @@ -471,10 +618,50 @@ public: \tparam SourceAllocator allocator of \c rhs \param rhs Value to copy from (read-only) \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) \see CopyFrom() */ - template< typename SourceAllocator > - GenericValue(const GenericValue& rhs, Allocator & allocator); + template + GenericValue(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + switch (rhs.GetType()) { + case kObjectType: { + SizeType count = rhs.data_.o.size; + Member* lm = reinterpret_cast(allocator.Malloc(count * sizeof(Member))); + const typename GenericValue::Member* rm = rhs.GetMembersPointer(); + for (SizeType i = 0; i < count; i++) { + new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings); + new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings); + } + data_.f.flags = kObjectFlag; + data_.o.size = data_.o.capacity = count; + SetMembersPointer(lm); + } + break; + case kArrayType: { + SizeType count = rhs.data_.a.size; + GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); + const GenericValue* re = rhs.GetElementsPointer(); + for (SizeType i = 0; i < count; i++) + new (&le[i]) GenericValue(re[i], allocator, copyConstStrings); + data_.f.flags = kArrayFlag; + data_.a.size = data_.a.capacity = count; + SetElementsPointer(le); + } + break; + case kStringType: + if (rhs.data_.f.flags == kConstStringFlag && !copyConstStrings) { + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + } + else + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + break; + default: + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + break; + } + } //! Constructor for boolean value. /*! \param b Boolean value @@ -484,96 +671,125 @@ public: */ #ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen template - explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT + explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT // See #472 #else explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT #endif - : data_(), flags_(b ? kTrueFlag : kFalseFlag) { + : data_() { // safe-guard against failing SFINAE RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + data_.f.flags = b ? kTrueFlag : kFalseFlag; } //! Constructor for int value. - explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberIntFlag) { + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() { data_.n.i64 = i; - if (i >= 0) - flags_ |= kUintFlag | kUint64Flag; + data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag; } //! Constructor for unsigned value. - explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUintFlag) { + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() { data_.n.u64 = u; - if (!(u & 0x80000000)) - flags_ |= kIntFlag | kInt64Flag; + data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag); } //! Constructor for int64_t value. - explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberInt64Flag) { + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() { data_.n.i64 = i64; + data_.f.flags = kNumberInt64Flag; if (i64 >= 0) { - flags_ |= kNumberUint64Flag; + data_.f.flags |= kNumberUint64Flag; if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) - flags_ |= kUintFlag; + data_.f.flags |= kUintFlag; if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) - flags_ |= kIntFlag; + data_.f.flags |= kIntFlag; } else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) - flags_ |= kIntFlag; + data_.f.flags |= kIntFlag; } //! Constructor for uint64_t value. - explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUint64Flag) { + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() { data_.n.u64 = u64; + data_.f.flags = kNumberUint64Flag; if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) - flags_ |= kInt64Flag; + data_.f.flags |= kInt64Flag; if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) - flags_ |= kUintFlag; + data_.f.flags |= kUintFlag; if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) - flags_ |= kIntFlag; + data_.f.flags |= kIntFlag; } //! Constructor for double value. - explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; } + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for float value. + explicit GenericValue(float f) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = static_cast(f); data_.f.flags = kNumberDoubleFlag; } //! Constructor for constant string (i.e. do not make a copy of string) - GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(StringRef(s, length)); } + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); } //! Constructor for constant string (i.e. do not make a copy of string) - explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(s); } + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); } //! Constructor for copy-string (i.e. do make a copy of string) - GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s, length), allocator); } + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); } //! Constructor for copy-string (i.e. do make a copy of string) - GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } + GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } #if RAPIDJSON_HAS_STDSTRING //! Constructor for copy-string from a string object (i.e. do make a copy of string) /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. */ - GenericValue(const std::basic_string& s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } + GenericValue(const std::basic_string& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } #endif + //! Constructor for Array. + /*! + \param a An array obtained by \c GetArray(). + \note \c Array is always pass-by-value. + \note the source array is moved into this value and the sourec array becomes empty. + */ + GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) { + a.value_.data_ = Data(); + a.value_.data_.f.flags = kArrayFlag; + } + + //! Constructor for Object. + /*! + \param o An object obtained by \c GetObject(). + \note \c Object is always pass-by-value. + \note the source object is moved into this value and the sourec object becomes empty. + */ + GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) { + o.value_.data_ = Data(); + o.value_.data_.f.flags = kObjectFlag; + } + //! Destructor. /*! Need to destruct elements of array, members of object, or copy-string. */ ~GenericValue() { if (Allocator::kNeedFree) { // Shortcut by Allocator's trait - switch(flags_) { + switch(data_.f.flags) { case kArrayFlag: - for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) - v->~GenericValue(); - Allocator::Free(data_.a.elements); + { + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + Allocator::Free(e); + } break; case kObjectFlag: for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) m->~Member(); - Allocator::Free(data_.o.members); + Allocator::Free(GetMembersPointer()); break; case kCopyStringFlag: - Allocator::Free(const_cast(data_.s.str)); + Allocator::Free(const_cast(GetStringPointer())); break; default: @@ -638,12 +854,13 @@ public: \tparam SourceAllocator Allocator type of \c rhs \param rhs Value to copy from (read-only) \param allocator Allocator to use for copying + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) */ template - GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator) { - RAPIDJSON_ASSERT((void*)this != (void const*)&rhs); + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); this->~GenericValue(); - new (this) GenericValue(rhs, allocator); + new (this) GenericValue(rhs, allocator, copyConstStrings); return *this; } @@ -660,6 +877,20 @@ public: return *this; } + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.value, b.value); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericValue& a, GenericValue& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + //! Prepare Value for move semantics /*! \return *this */ GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } @@ -709,7 +940,7 @@ public: else return data_.n.u64 == rhs.data_.n.u64; - default: // kTrueType, kFalseType, kNullType + default: return true; } } @@ -757,20 +988,58 @@ public: //!@name Type //@{ - Type GetType() const { return static_cast(flags_ & kTypeMask); } - bool IsNull() const { return flags_ == kNullFlag; } - bool IsFalse() const { return flags_ == kFalseFlag; } - bool IsTrue() const { return flags_ == kTrueFlag; } - bool IsBool() const { return (flags_ & kBoolFlag) != 0; } - bool IsObject() const { return flags_ == kObjectFlag; } - bool IsArray() const { return flags_ == kArrayFlag; } - bool IsNumber() const { return (flags_ & kNumberFlag) != 0; } - bool IsInt() const { return (flags_ & kIntFlag) != 0; } - bool IsUint() const { return (flags_ & kUintFlag) != 0; } - bool IsInt64() const { return (flags_ & kInt64Flag) != 0; } - bool IsUint64() const { return (flags_ & kUint64Flag) != 0; } - bool IsDouble() const { return (flags_ & kDoubleFlag) != 0; } - bool IsString() const { return (flags_ & kStringFlag) != 0; } + Type GetType() const { return static_cast(data_.f.flags & kTypeMask); } + bool IsNull() const { return data_.f.flags == kNullFlag; } + bool IsFalse() const { return data_.f.flags == kFalseFlag; } + bool IsTrue() const { return data_.f.flags == kTrueFlag; } + bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; } + bool IsObject() const { return data_.f.flags == kObjectFlag; } + bool IsArray() const { return data_.f.flags == kArrayFlag; } + bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; } + bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; } + bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; } + bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; } + bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; } + bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; } + bool IsString() const { return (data_.f.flags & kStringFlag) != 0; } + + // Checks whether a number can be losslessly converted to a double. + bool IsLosslessDouble() const { + if (!IsNumber()) return false; + if (IsUint64()) { + uint64_t u = GetUint64(); + volatile double d = static_cast(u); + return (d >= 0.0) + && (d < static_cast((std::numeric_limits::max)())) + && (u == static_cast(d)); + } + if (IsInt64()) { + int64_t i = GetInt64(); + volatile double d = static_cast(i); + return (d >= static_cast((std::numeric_limits::min)())) + && (d < static_cast((std::numeric_limits::max)())) + && (i == static_cast(d)); + } + return true; // double, int, uint are always lossless + } + + // Checks whether a number is a float (possible lossy). + bool IsFloat() const { + if ((data_.f.flags & kDoubleFlag) == 0) + return false; + double d = GetDouble(); + return d >= -3.4028234e38 && d <= 3.4028234e38; + } + // Checks whether a number can be losslessly converted to a float. + bool IsLosslessFloat() const { + if (!IsNumber()) return false; + double a = GetDouble(); + if (a < static_cast(-(std::numeric_limits::max)()) + || a > static_cast((std::numeric_limits::max)())) + return false; + double b = static_cast(static_cast(a)); + return a >= b && a <= b; // Prevent -Wfloat-equal + } //@} @@ -784,7 +1053,7 @@ public: //!@name Bool //@{ - bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return flags_ == kTrueFlag; } + bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; } //!< Set boolean value /*! \post IsBool() == true */ GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } @@ -837,8 +1106,14 @@ public: return member->value; else { RAPIDJSON_ASSERT(false); // see above note - static GenericValue NullValue; - return NullValue; + + // This will generate -Wexit-time-destructors in clang + // static GenericValue NullValue; + // return NullValue; + + // Use static buffer and placement-new to prevent destruction + static char buffer[sizeof(GenericValue)]; + return *new (buffer) GenericValue(); } } template @@ -852,16 +1127,16 @@ public: //! Const member iterator /*! \pre IsObject() == true */ - ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members); } + ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); } //! Const \em past-the-end member iterator /*! \pre IsObject() == true */ - ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members + data_.o.size); } + ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); } //! Member iterator /*! \pre IsObject() == true */ - MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members); } + MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); } //! \em Past-the-end member iterator /*! \pre IsObject() == true */ - MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members + data_.o.size); } + MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } //! Check whether a member exists in the object. /*! @@ -949,8 +1224,8 @@ public: \return Iterator to member, if it exists. Otherwise returns \ref MemberEnd(). */ - MemberIterator FindMember(const std::basic_string& name) { return FindMember(StringRef(name)); } - ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(StringRef(name)); } + MemberIterator FindMember(const std::basic_string& name) { return FindMember(GenericValue(StringRef(name))); } + ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(GenericValue(StringRef(name))); } #endif //! Add a member (name-value pair) to the object. @@ -967,20 +1242,21 @@ public: RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(name.IsString()); - Object& o = data_.o; + ObjectData& o = data_.o; if (o.size >= o.capacity) { if (o.capacity == 0) { o.capacity = kDefaultObjectCapacity; - o.members = reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member))); + SetMembersPointer(reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member)))); } else { SizeType oldCapacity = o.capacity; o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5 - o.members = reinterpret_cast(allocator.Realloc(o.members, oldCapacity * sizeof(Member), o.capacity * sizeof(Member))); + SetMembersPointer(reinterpret_cast(allocator.Realloc(GetMembersPointer(), oldCapacity * sizeof(Member), o.capacity * sizeof(Member)))); } } - o.members[o.size].name.RawAssign(name); - o.members[o.size].value.RawAssign(value); + Member* members = GetMembersPointer(); + members[o.size].name.RawAssign(name); + members[o.size].value.RawAssign(value); o.size++; return *this; } @@ -1159,18 +1435,14 @@ public: MemberIterator RemoveMember(MemberIterator m) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(data_.o.size > 0); - RAPIDJSON_ASSERT(data_.o.members != 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); - MemberIterator last(data_.o.members + (data_.o.size - 1)); - if (data_.o.size > 1 && m != last) { - // Move the last one to this place - *m = *last; - } - else { - // Only one left, just destroy - m->~Member(); - } + MemberIterator last(GetMembersPointer() + (data_.o.size - 1)); + if (data_.o.size > 1 && m != last) + *m = *last; // Move the last one to this place + else + m->~Member(); // Only one left, just destroy --data_.o.size; return m; } @@ -1200,7 +1472,7 @@ public: MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(data_.o.size > 0); - RAPIDJSON_ASSERT(data_.o.members != 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); RAPIDJSON_ASSERT(first >= MemberBegin()); RAPIDJSON_ASSERT(first <= last); RAPIDJSON_ASSERT(last <= MemberEnd()); @@ -1208,11 +1480,39 @@ public: MemberIterator pos = MemberBegin() + (first - MemberBegin()); for (MemberIterator itr = pos; itr != last; ++itr) itr->~Member(); - std::memmove(&*pos, &*last, (MemberEnd() - last) * sizeof(Member)); - data_.o.size -= (last - first); + std::memmove(&*pos, &*last, static_cast(MemberEnd() - last) * sizeof(Member)); + data_.o.size -= static_cast(last - first); return pos; } + //! Erase a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note Linear time complexity. + */ + bool EraseMember(const Ch* name) { + GenericValue n(StringRef(name)); + return EraseMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) { return EraseMember(GenericValue(StringRef(name))); } +#endif + + template + bool EraseMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + EraseMember(m); + return true; + } + else + return false; + } + + Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } + ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } + //@} //!@name Array @@ -1220,7 +1520,7 @@ public: //! Set this value as an empty array. /*! \post IsArray == true */ - GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } + GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } //! Get the number of elements in array. SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } @@ -1237,8 +1537,9 @@ public: */ void Clear() { RAPIDJSON_ASSERT(IsArray()); - for (SizeType i = 0; i < data_.a.size; ++i) - data_.a.elements[i].~GenericValue(); + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); data_.a.size = 0; } @@ -1250,16 +1551,16 @@ public: GenericValue& operator[](SizeType index) { RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(index < data_.a.size); - return data_.a.elements[index]; + return GetElementsPointer()[index]; } const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } //! Element iterator /*! \pre IsArray() == true */ - ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements; } + ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); } //! \em Past-the-end element iterator /*! \pre IsArray() == true */ - ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements + data_.a.size; } + ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; } //! Constant element iterator /*! \pre IsArray() == true */ ConstValueIterator Begin() const { return const_cast(*this).Begin(); } @@ -1276,7 +1577,7 @@ public: GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { RAPIDJSON_ASSERT(IsArray()); if (newCapacity > data_.a.capacity) { - data_.a.elements = (GenericValue*)allocator.Realloc(data_.a.elements, data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)); + SetElementsPointer(reinterpret_cast(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)))); data_.a.capacity = newCapacity; } return *this; @@ -1296,7 +1597,7 @@ public: RAPIDJSON_ASSERT(IsArray()); if (data_.a.size >= data_.a.capacity) Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); - data_.a.elements[data_.a.size++].RawAssign(value); + GetElementsPointer()[data_.a.size++].RawAssign(value); return *this; } @@ -1350,7 +1651,7 @@ public: GenericValue& PopBack() { RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(!Empty()); - data_.a.elements[--data_.a.size].~GenericValue(); + GetElementsPointer()[--data_.a.size].~GenericValue(); return *this; } @@ -1376,35 +1677,48 @@ public: ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(data_.a.size > 0); - RAPIDJSON_ASSERT(data_.a.elements != 0); + RAPIDJSON_ASSERT(GetElementsPointer() != 0); RAPIDJSON_ASSERT(first >= Begin()); RAPIDJSON_ASSERT(first <= last); RAPIDJSON_ASSERT(last <= End()); ValueIterator pos = Begin() + (first - Begin()); for (ValueIterator itr = pos; itr != last; ++itr) itr->~GenericValue(); - std::memmove(pos, last, (End() - last) * sizeof(GenericValue)); - data_.a.size -= (last - first); + std::memmove(pos, last, static_cast(End() - last) * sizeof(GenericValue)); + data_.a.size -= static_cast(last - first); return pos; } + Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); } + ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); } + //@} //!@name Number //@{ - int GetInt() const { RAPIDJSON_ASSERT(flags_ & kIntFlag); return data_.n.i.i; } - unsigned GetUint() const { RAPIDJSON_ASSERT(flags_ & kUintFlag); return data_.n.u.u; } - int64_t GetInt64() const { RAPIDJSON_ASSERT(flags_ & kInt64Flag); return data_.n.i64; } - uint64_t GetUint64() const { RAPIDJSON_ASSERT(flags_ & kUint64Flag); return data_.n.u64; } + int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; } + //! Get the value as double type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless. + */ double GetDouble() const { RAPIDJSON_ASSERT(IsNumber()); - if ((flags_ & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. - if ((flags_ & kIntFlag) != 0) return data_.n.i.i; // int -> double - if ((flags_ & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double - if ((flags_ & kInt64Flag) != 0) return (double)data_.n.i64; // int64_t -> double (may lose precision) - RAPIDJSON_ASSERT((flags_ & kUint64Flag) != 0); return (double)data_.n.u64; // uint64_t -> double (may lose precision) + if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((data_.f.flags & kInt64Flag) != 0) return static_cast(data_.n.i64); // int64_t -> double (may lose precision) + RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast(data_.n.u64); // uint64_t -> double (may lose precision) + } + + //! Get the value as float type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless. + */ + float GetFloat() const { + return static_cast(GetDouble()); } GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } @@ -1412,18 +1726,19 @@ public: GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } + GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(static_cast(f)); return *this; } //@} //!@name String //@{ - const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? data_.ss.str : data_.s.str); } + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); } //! Get the length of string. /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). */ - SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } //! Set this value as a string without copying source string. /*! This version has better performance with supplied length, and also support string containing null character. @@ -1450,7 +1765,7 @@ public: \return The value itself for fluent API. \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length */ - GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; } + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { return SetString(StringRef(s, length), allocator); } //! Set this value as a string by copying from source string. /*! \param s source string. @@ -1458,7 +1773,15 @@ public: \return The value itself for fluent API. \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length */ - GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(StringRef(s), allocator); } + + //! Set this value as a string by copying from source string. + /*! \param s source string reference + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(StringRefType s, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, allocator); return *this; } #if RAPIDJSON_HAS_STDSTRING //! Set this value as a string by copying from source string. @@ -1468,11 +1791,35 @@ public: \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. */ - GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(s.data(), SizeType(s.size()), allocator); } + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(StringRef(s), allocator); } #endif //@} + //!@name Array + //@{ + + //! Templated version for checking whether this value is type T. + /*! + \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string + */ + template + bool Is() const { return internal::TypeHelper::Is(*this); } + + template + T Get() const { return internal::TypeHelper::Get(*this); } + + template + T Get() { return internal::TypeHelper::Get(*this); } + + template + ValueType& Set(const T& data) { return internal::TypeHelper::Set(*this, data); } + + template + ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper::Set(*this, data, allocator); } + + //@} + //! Generate events of this value to a Handler. /*! This function adopts the GoF visitor pattern. Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. @@ -1488,35 +1835,35 @@ public: case kTrueType: return handler.Bool(true); case kObjectType: - if (!handler.StartObject()) + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) return false; for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. - if (!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.flags_ & kCopyFlag) != 0)) + if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0))) return false; - if (!m->value.Accept(handler)) + if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler))) return false; } return handler.EndObject(data_.o.size); case kArrayType: - if (!handler.StartArray()) + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) return false; - for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) - if (!v->Accept(handler)) + for (const GenericValue* v = Begin(); v != End(); ++v) + if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) return false; return handler.EndArray(data_.a.size); case kStringType: - return handler.String(GetString(), GetStringLength(), (flags_ & kCopyFlag) != 0); + return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0); default: RAPIDJSON_ASSERT(GetType() == kNumberType); - if (IsInt()) return handler.Int(data_.n.i.i); + if (IsDouble()) return handler.Double(data_.n.d); + else if (IsInt()) return handler.Int(data_.n.i.i); else if (IsUint()) return handler.Uint(data_.n.u.u); else if (IsInt64()) return handler.Int64(data_.n.i64); - else if (IsUint64()) return handler.Uint64(data_.n.u64); - else return handler.Double(data_.n.d); + else return handler.Uint64(data_.n.u64); } } @@ -1525,16 +1872,16 @@ private: template friend class GenericDocument; enum { - kBoolFlag = 0x100, - kNumberFlag = 0x200, - kIntFlag = 0x400, - kUintFlag = 0x800, - kInt64Flag = 0x1000, - kUint64Flag = 0x2000, - kDoubleFlag = 0x4000, - kStringFlag = 0x100000, - kCopyFlag = 0x200000, - kInlineStrFlag = 0x400000, + kBoolFlag = 0x0008, + kNumberFlag = 0x0010, + kIntFlag = 0x0020, + kUintFlag = 0x0040, + kInt64Flag = 0x0080, + kUint64Flag = 0x0100, + kDoubleFlag = 0x0200, + kStringFlag = 0x0400, + kCopyFlag = 0x0800, + kInlineStrFlag = 0x1000, // Initial flags of different types. kNullFlag = kNullType, @@ -1552,16 +1899,27 @@ private: kObjectFlag = kObjectType, kArrayFlag = kArrayType, - kTypeMask = 0xFF // bitwise-and with mask of 0xFF can be optimized by compiler + kTypeMask = 0x07 }; static const SizeType kDefaultArrayCapacity = 16; static const SizeType kDefaultObjectCapacity = 16; + struct Flag { +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION + char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer +#elif RAPIDJSON_64BIT + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes +#else + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes +#endif + uint16_t flags; + }; + struct String { - const Ch* str; SizeType length; - unsigned hashcode; //!< reserved + SizeType hashcode; //!< reserved + const Ch* str; }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars @@ -1570,15 +1928,15 @@ private: // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as // the string terminator as well. For getting the string length back from that value just use // "MaxSize - str[LenPos]". - // This allows to store 11-chars strings in 32-bit mode and 15-chars strings in 64-bit mode - // inline (for `UTF8`-encoded strings). + // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode, + // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings). struct ShortString { - enum { MaxChars = sizeof(String) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + enum { MaxChars = sizeof(static_cast(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; Ch str[MaxChars]; - inline static bool Usable(SizeType len) { return (MaxSize >= len); } - inline void SetLength(SizeType len) { str[LenPos] = (Ch)(MaxSize - len); } - inline SizeType GetLength() const { return (SizeType)(MaxSize - str[LenPos]); } + inline static bool Usable(SizeType len) { return (MaxSize >= len); } + inline void SetLength(SizeType len) { str[LenPos] = static_cast(MaxSize - len); } + inline SizeType GetLength() const { return static_cast(MaxSize - str[LenPos]); } }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode // By using proper binary layout, retrieval of different integer types do not need conversions. @@ -1607,69 +1965,79 @@ private: double d; }; // 8 bytes - struct Object { - Member* members; + struct ObjectData { SizeType size; SizeType capacity; + Member* members; }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode - struct Array { - GenericValue* elements; + struct ArrayData { SizeType size; SizeType capacity; + GenericValue* elements; }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode union Data { String s; ShortString ss; Number n; - Object o; - Array a; - }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + ObjectData o; + ArrayData a; + Flag f; + }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION + + RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); } + RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); } + RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); } + RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } + RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); } + RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } // Initialize this value as array with initial data, without calling destructor. void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { - flags_ = kArrayFlag; + data_.f.flags = kArrayFlag; if (count) { - data_.a.elements = (GenericValue*)allocator.Malloc(count * sizeof(GenericValue)); - std::memcpy(data_.a.elements, values, count * sizeof(GenericValue)); + GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); + SetElementsPointer(e); + std::memcpy(e, values, count * sizeof(GenericValue)); } else - data_.a.elements = NULL; + SetElementsPointer(0); data_.a.size = data_.a.capacity = count; } //! Initialize this value as object with initial data, without calling destructor. void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { - flags_ = kObjectFlag; + data_.f.flags = kObjectFlag; if (count) { - data_.o.members = (Member*)allocator.Malloc(count * sizeof(Member)); - std::memcpy(data_.o.members, members, count * sizeof(Member)); + Member* m = static_cast(allocator.Malloc(count * sizeof(Member))); + SetMembersPointer(m); + std::memcpy(m, members, count * sizeof(Member)); } else - data_.o.members = NULL; + SetMembersPointer(0); data_.o.size = data_.o.capacity = count; } //! Initialize this value as constant string, without calling destructor. void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { - flags_ = kConstStringFlag; - data_.s.str = s; + data_.f.flags = kConstStringFlag; + SetStringPointer(s); data_.s.length = s.length; } //! Initialize this value as copy string with initial data, without calling destructor. void SetStringRaw(StringRefType s, Allocator& allocator) { - Ch* str = NULL; - if(ShortString::Usable(s.length)) { - flags_ = kShortStringFlag; + Ch* str = 0; + if (ShortString::Usable(s.length)) { + data_.f.flags = kShortStringFlag; data_.ss.SetLength(s.length); str = data_.ss.str; } else { - flags_ = kCopyStringFlag; + data_.f.flags = kCopyStringFlag; data_.s.length = s.length; - str = (Ch *)allocator.Malloc((s.length + 1) * sizeof(Ch)); - data_.s.str = str; + str = static_cast(allocator.Malloc((s.length + 1) * sizeof(Ch))); + SetStringPointer(str); } std::memcpy(str, s, s.length * sizeof(Ch)); str[s.length] = '\0'; @@ -1678,8 +2046,8 @@ private: //! Assignment without calling destructor void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { data_ = rhs.data_; - flags_ = rhs.flags_; - rhs.flags_ = kNullFlag; + // data_.f.flags = rhs.data_.f.flags; + rhs.data_.f.flags = kNullFlag; } template @@ -1699,7 +2067,6 @@ private: } Data data_; - unsigned flags_; }; //! GenericValue with UTF8 encoding @@ -1724,7 +2091,22 @@ public: typedef Allocator AllocatorType; //!< Allocator type from template parameter. //! Constructor - /*! \param allocator Optional allocator for allocating memory. + /*! Creates an empty document of specified type. + \param type Mandatory type of object to create. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + } + + //! Constructor + /*! Creates an empty document which type is Null. + \param allocator Optional allocator for allocating memory. \param stackCapacity Optional initial capacity of stack in bytes. \param stackAllocator Optional allocator for allocating memory for stack. */ @@ -1732,13 +2114,13 @@ public: allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Move constructor in C++11 GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT - : ValueType(std::move(rhs)), + : ValueType(std::forward(rhs)), // explicit cast to avoid prohibited move from Document allocator_(rhs.allocator_), ownAllocator_(rhs.ownAllocator_), stack_(std::move(rhs.stack_)), @@ -1778,6 +2160,54 @@ public: } #endif + //! Exchange the contents of this document with those of another. + /*! + \param rhs Another document. + \note Constant complexity. + \see GenericValue::Swap + */ + GenericDocument& Swap(GenericDocument& rhs) RAPIDJSON_NOEXCEPT { + ValueType::Swap(rhs); + stack_.Swap(rhs.stack_); + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(parseResult_, rhs.parseResult_); + return *this; + } + + // Allow Swap with ValueType. + // Refer to Effective C++ 3rd Edition/Item 33: Avoid hiding inherited names. + using ValueType::Swap; + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.doc, b.doc); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Populate this document by a generator which produces SAX events. + /*! \tparam Generator A functor with bool f(Handler) prototype. + \param g Generator functor which sends SAX events to the parameter. + \return The document itself for fluent API. + */ + template + GenericDocument& Populate(Generator& g) { + ClearStackOnExit scope(*this); + if (g(*this)) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + //!@name Parse from stream //!@{ @@ -1790,13 +2220,13 @@ public: */ template GenericDocument& ParseStream(InputStream& is) { - ValueType::SetNull(); // Remove existing root if exist - GenericReader reader(&stack_.GetAllocator()); + GenericReader reader( + stack_.HasAllocator() ? &stack_.GetAllocator() : 0); ClearStackOnExit scope(*this); parseResult_ = reader.template Parse(is, *this); if (parseResult_) { RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object - this->RawAssign(*stack_.template Pop(1)); // Add this-> to prevent issue 13. + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document } return *this; } @@ -1855,7 +2285,7 @@ public: \param str Read-only zero-terminated string to be parsed. */ template - GenericDocument& Parse(const Ch* str) { + GenericDocument& Parse(const typename SourceEncoding::Ch* str) { RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); GenericStringStream s(str); return ParseStream(s); @@ -1876,6 +2306,42 @@ public: GenericDocument& Parse(const Ch* str) { return Parse(str); } + + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + MemoryStream ms(reinterpret_cast(str), length * sizeof(typename SourceEncoding::Ch)); + EncodedInputStream is(ms); + ParseStream(is); + return *this; + } + + template + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + +#if RAPIDJSON_HAS_STDSTRING + template + GenericDocument& Parse(const std::basic_string& str) { + // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t) + return Parse(str.c_str()); + } + + template + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str.c_str()); + } + + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str); + } +#endif // RAPIDJSON_HAS_STDSTRING + //!@} //!@name Handling parse errors @@ -1890,10 +2356,26 @@ public: //! Get the position of last parsing error in input, 0 otherwise. size_t GetErrorOffset() const { return parseResult_.Offset(); } + //! Implicit conversion to get the last parse result +#ifndef __clang // -Wdocumentation + /*! \return \ref ParseResult of the last parse operation + + \code + Document doc; + ParseResult ok = doc.Parse(json); + if (!ok) + printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset()); + \endcode + */ +#endif + operator ParseResult() const { return parseResult_; } //!@} //! Get the allocator of this document. - Allocator& GetAllocator() { return *allocator_; } + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } //! Get the capacity of stack in bytes. size_t GetStackCapacity() const { return stack_.GetCapacity(); } @@ -1910,9 +2392,10 @@ private: }; // callers of the following private Handler functions - template friend class GenericReader; // for parsing + // template friend class GenericReader; // for parsing template friend class GenericValue; // for deep copying +public: // Implementation of Handler bool Null() { new (stack_.template Push()) ValueType(); return true; } bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } @@ -1922,6 +2405,14 @@ private: bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } + bool RawNumber(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + bool String(const Ch* str, SizeType length, bool copy) { if (copy) new (stack_.template Push()) ValueType(str, length, GetAllocator()); @@ -1936,7 +2427,7 @@ private: bool EndObject(SizeType memberCount) { typename ValueType::Member* members = stack_.template Pop(memberCount); - stack_.template Top()->SetObjectRaw(members, (SizeType)memberCount, GetAllocator()); + stack_.template Top()->SetObjectRaw(members, memberCount, GetAllocator()); return true; } @@ -1977,38 +2468,146 @@ private: //! GenericDocument with UTF8 encoding typedef GenericDocument > Document; -// defined here due to the dependency on GenericDocument -template -template -inline -GenericValue::GenericValue(const GenericValue& rhs, Allocator& allocator) -{ - switch (rhs.GetType()) { - case kObjectType: - case kArrayType: { // perform deep copy via SAX Handler - GenericDocument d(&allocator); - rhs.Accept(d); - RawAssign(*d.stack_.template Pop(1)); - } - break; - case kStringType: - if (rhs.flags_ == kConstStringFlag) { - flags_ = rhs.flags_; - data_ = *reinterpret_cast(&rhs.data_); - } else { - SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); - } - break; - default: // kNumberType, kTrueType, kFalseType, kNullType - flags_ = rhs.flags_; - data_ = *reinterpret_cast(&rhs.data_); - } -} +//! Helper class for accessing Value of array type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetArray(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericArray { +public: + typedef GenericArray ConstArray; + typedef GenericArray Array; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef ValueType* ValueIterator; // This may be const or non-const iterator + typedef const ValueT* ConstValueIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; -RAPIDJSON_NAMESPACE_END + template + friend class GenericValue; -#if defined(_MSC_VER) || defined(__GNUC__) -RAPIDJSON_DIAG_POP + GenericArray(const GenericArray& rhs) : value_(rhs.value_) {} + GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } + ~GenericArray() {} + + SizeType Size() const { return value_.Size(); } + SizeType Capacity() const { return value_.Capacity(); } + bool Empty() const { return value_.Empty(); } + void Clear() const { value_.Clear(); } + ValueType& operator[](SizeType index) const { return value_[index]; } + ValueIterator Begin() const { return value_.Begin(); } + ValueIterator End() const { return value_.End(); } + GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; } + GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + GenericArray PopBack() const { value_.PopBack(); return *this; } + ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); } + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + ValueIterator begin() const { return value_.Begin(); } + ValueIterator end() const { return value_.End(); } #endif +private: + GenericArray(); + GenericArray(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +//! Helper class for accessing Value of object type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetObject(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericObject { +public: + typedef GenericObject ConstObject; + typedef GenericObject Object; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef GenericMemberIterator MemberIterator; // This may be const or non-const iterator + typedef GenericMemberIterator ConstMemberIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename ValueType::Ch Ch; + + template + friend class GenericValue; + + GenericObject(const GenericObject& rhs) : value_(rhs.value_) {} + GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } + ~GenericObject() {} + + SizeType MemberCount() const { return value_.MemberCount(); } + bool ObjectEmpty() const { return value_.ObjectEmpty(); } + template ValueType& operator[](T* name) const { return value_[name]; } + template ValueType& operator[](const GenericValue& name) const { return value_[name]; } +#if RAPIDJSON_HAS_STDSTRING + ValueType& operator[](const std::basic_string& name) const { return value_[name]; } +#endif + MemberIterator MemberBegin() const { return value_.MemberBegin(); } + MemberIterator MemberEnd() const { return value_.MemberEnd(); } + bool HasMember(const Ch* name) const { return value_.HasMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } +#endif + template bool HasMember(const GenericValue& name) const { return value_.HasMember(name); } + MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); } + template MemberIterator FindMember(const GenericValue& name) const { return value_.FindMember(name); } +#if RAPIDJSON_HAS_STDSTRING + MemberIterator FindMember(const std::basic_string& name) const { return value_.FindMember(name); } +#endif + GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_STDSTRING + GenericObject AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + void RemoveAllMembers() { value_.RemoveAllMembers(); } + bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } +#endif + template bool RemoveMember(const GenericValue& name) const { return value_.RemoveMember(name); } + MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); } + MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); } + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); } + bool EraseMember(const Ch* name) const { return value_.EraseMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } +#endif + template bool EraseMember(const GenericValue& name) const { return value_.EraseMember(name); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + MemberIterator begin() const { return value_.MemberBegin(); } + MemberIterator end() const { return value_.MemberEnd(); } +#endif + +private: + GenericObject(); + GenericObject(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + #endif // RAPIDJSON_DOCUMENT_H_ diff --git a/contrib/rapidjson/include/rapidjson/encodedstream.h b/contrib/rapidjson/include/rapidjson/encodedstream.h index 7c8863fee..223601c05 100644 --- a/contrib/rapidjson/include/rapidjson/encodedstream.h +++ b/contrib/rapidjson/include/rapidjson/encodedstream.h @@ -15,13 +15,19 @@ #ifndef RAPIDJSON_ENCODEDSTREAM_H_ #define RAPIDJSON_ENCODEDSTREAM_H_ -#include "rapidjson.h" +#include "stream.h" +#include "memorystream.h" #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + RAPIDJSON_NAMESPACE_BEGIN //! Input byte stream wrapper with a statically bound encoding. @@ -57,10 +63,38 @@ private: Ch current_; }; +//! Specialized for UTF8 MemoryStream. +template <> +class EncodedInputStream, MemoryStream> { +public: + typedef UTF8<>::Ch Ch; + + EncodedInputStream(MemoryStream& is) : is_(is) { + if (static_cast(is_.Peek()) == 0xEFu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBBu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBFu) is_.Take(); + } + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) {} + void Flush() {} + Ch* PutBegin() { return 0; } + size_t PutEnd(Ch*) { return 0; } + + MemoryStream& is_; + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); +}; + //! Output byte stream wrapper with statically bound encoding. /*! \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. - \tparam InputByteStream Type of input byte stream. For example, FileWriteStream. + \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream. */ template class EncodedOutputStream { @@ -77,8 +111,8 @@ public: void Flush() { os_.Flush(); } // Not implemented - Ch Peek() const { RAPIDJSON_ASSERT(false); } - Ch Take() { RAPIDJSON_ASSERT(false); } + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } @@ -142,11 +176,11 @@ private: // FF FE UTF-16LE // EF BB BF UTF-8 - const unsigned char* c = (const unsigned char *)is_->Peek4(); + const unsigned char* c = reinterpret_cast(is_->Peek4()); if (!c) return; - unsigned bom = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24); + unsigned bom = static_cast(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)); hasBOM_ = false; if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } @@ -166,7 +200,7 @@ private: // xx xx xx xx UTF-8 if (!hasBOM_) { - unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + int pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); switch (pattern) { case 0x08: type_ = kUTF32BE; break; case 0x0A: type_ = kUTF16BE; break; @@ -193,7 +227,7 @@ private: //! Output stream wrapper with dynamically bound encoding and automatic encoding detection. /*! \tparam CharType Type of character for writing. - \tparam InputByteStream type of output byte stream to be wrapped. + \tparam OutputByteStream type of output byte stream to be wrapped. */ template class AutoUTFOutputStream { @@ -227,8 +261,8 @@ public: void Flush() { os_->Flush(); } // Not implemented - Ch Peek() const { RAPIDJSON_ASSERT(false); } - Ch Take() { RAPIDJSON_ASSERT(false); } + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } @@ -254,6 +288,10 @@ private: RAPIDJSON_NAMESPACE_END +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif diff --git a/contrib/rapidjson/include/rapidjson/encodings.h b/contrib/rapidjson/include/rapidjson/encodings.h index 90b46ed32..0df1c3435 100644 --- a/contrib/rapidjson/include/rapidjson/encodings.h +++ b/contrib/rapidjson/include/rapidjson/encodings.h @@ -120,19 +120,45 @@ struct UTF8 { } } + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + PutUnsafe(os, static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + PutUnsafe(os, static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + PutUnsafe(os, static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + } + template static bool Decode(InputStream& is, unsigned* codepoint) { -#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | ((unsigned char)c & 0x3Fu) -#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0) +#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) +#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) #define TAIL() COPY(); TRANS(0x70) - Ch c = is.Take(); + typename InputStream::Ch c = is.Take(); if (!(c & 0x80)) { - *codepoint = (unsigned char)c; + *codepoint = static_cast(c); return true; } - unsigned char type = GetRange((unsigned char)c); - *codepoint = (0xFF >> type) & (unsigned char)c; + unsigned char type = GetRange(static_cast(c)); + if (type >= 32) { + *codepoint = 0; + } else { + *codepoint = (0xFFu >> type) & static_cast(c); + } bool result = true; switch (type) { case 2: TAIL(); return result; @@ -152,7 +178,7 @@ struct UTF8 { template static bool Validate(InputStream& is, OutputStream& os) { #define COPY() os.Put(c = is.Take()) -#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0) +#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) #define TAIL() COPY(); TRANS(0x70) Ch c; COPY(); @@ -160,7 +186,7 @@ struct UTF8 { return true; bool result = true; - switch (GetRange((unsigned char)c)) { + switch (GetRange(static_cast(c))) { case 2: TAIL(); return result; case 3: TAIL(); TAIL(); return result; case 4: COPY(); TRANS(0x50); TAIL(); return result; @@ -196,12 +222,12 @@ struct UTF8 { template static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - Ch c = Take(is); - if ((unsigned char)c != 0xEFu) return c; + typename InputByteStream::Ch c = Take(is); + if (static_cast(c) != 0xEFu) return c; c = is.Take(); - if ((unsigned char)c != 0xBBu) return c; + if (static_cast(c) != 0xBBu) return c; c = is.Take(); - if ((unsigned char)c != 0xBFu) return c; + if (static_cast(c) != 0xBFu) return c; c = is.Take(); return c; } @@ -209,13 +235,15 @@ struct UTF8 { template static Ch Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - return is.Take(); + return static_cast(is.Take()); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(0xEFu); os.Put(0xBBu); os.Put(0xBFu); + os.Put(static_cast(0xEFu)); + os.Put(static_cast(0xBBu)); + os.Put(static_cast(0xBFu)); } template @@ -255,22 +283,38 @@ struct UTF16 { RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); unsigned v = codepoint - 0x10000; os.Put(static_cast((v >> 10) | 0xD800)); - os.Put((v & 0x3FF) | 0xDC00); + os.Put(static_cast((v & 0x3FF) | 0xDC00)); + } + } + + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + PutUnsafe(os, static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + PutUnsafe(os, static_cast((v >> 10) | 0xD800)); + PutUnsafe(os, static_cast((v & 0x3FF) | 0xDC00)); } } template static bool Decode(InputStream& is, unsigned* codepoint) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); - Ch c = is.Take(); + typename InputStream::Ch c = is.Take(); if (c < 0xD800 || c > 0xDFFF) { - *codepoint = c; + *codepoint = static_cast(c); return true; } else if (c <= 0xDBFF) { - *codepoint = (c & 0x3FF) << 10; + *codepoint = (static_cast(c) & 0x3FF) << 10; c = is.Take(); - *codepoint |= (c & 0x3FF); + *codepoint |= (static_cast(c) & 0x3FF); *codepoint += 0x10000; return c >= 0xDC00 && c <= 0xDFFF; } @@ -281,8 +325,8 @@ struct UTF16 { static bool Validate(InputStream& is, OutputStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); - Ch c; - os.Put(c = is.Take()); + typename InputStream::Ch c; + os.Put(static_cast(c = is.Take())); if (c < 0xD800 || c > 0xDFFF) return true; else if (c <= 0xDBFF) { @@ -300,28 +344,29 @@ struct UTF16LE : UTF16 { static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); - return (unsigned short)c == 0xFEFFu ? Take(is) : c; + return static_cast(c) == 0xFEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = (unsigned char)is.Take(); - c |= (unsigned char)is.Take() << 8; - return c; + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + return static_cast(c); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(0xFFu); os.Put(0xFEu); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(c & 0xFFu); - os.Put((c >> 8) & 0xFFu); + os.Put(static_cast(static_cast(c) & 0xFFu)); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); } }; @@ -332,28 +377,29 @@ struct UTF16BE : UTF16 { static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); - return (unsigned short)c == 0xFEFFu ? Take(is) : c; + return static_cast(c) == 0xFEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = (unsigned char)is.Take() << 8; - c |= (unsigned char)is.Take(); - return c; + unsigned c = static_cast(static_cast(is.Take())) << 8; + c |= static_cast(is.Take()); + return static_cast(c); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(0xFEu); os.Put(0xFFu); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put((c >> 8) & 0xFFu); - os.Put(c & 0xFFu); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + os.Put(static_cast(static_cast(c) & 0xFFu)); } }; @@ -382,6 +428,13 @@ struct UTF32 { os.Put(codepoint); } + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, codepoint); + } + template static bool Decode(InputStream& is, unsigned* codepoint) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); @@ -406,32 +459,35 @@ struct UTF32LE : UTF32 { static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); - return (unsigned)c == 0x0000FEFFu ? Take(is) : c; + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = (unsigned char)is.Take(); - c |= (unsigned char)is.Take() << 8; - c |= (unsigned char)is.Take() << 16; - c |= (unsigned char)is.Take() << 24; - return c; + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 24; + return static_cast(c); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(0xFFu); os.Put(0xFEu); os.Put(0x00u); os.Put(0x00u); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(c & 0xFFu); - os.Put((c >> 8) & 0xFFu); - os.Put((c >> 16) & 0xFFu); - os.Put((c >> 24) & 0xFFu); + os.Put(static_cast(c & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 24) & 0xFFu)); } }; @@ -442,32 +498,35 @@ struct UTF32BE : UTF32 { static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); - return (unsigned)c == 0x0000FEFFu ? Take(is) : c; + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = (unsigned char)is.Take() << 24; - c |= (unsigned char)is.Take() << 16; - c |= (unsigned char)is.Take() << 8; - c |= (unsigned char)is.Take(); - return c; + unsigned c = static_cast(static_cast(is.Take())) << 24; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())); + return static_cast(c); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(0x00u); os.Put(0x00u); os.Put(0xFEu); os.Put(0xFFu); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put((c >> 24) & 0xFFu); - os.Put((c >> 16) & 0xFFu); - os.Put((c >> 8) & 0xFFu); - os.Put(c & 0xFFu); + os.Put(static_cast((c >> 24) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast(c & 0xFFu)); } }; @@ -491,31 +550,37 @@ struct ASCII { os.Put(static_cast(codepoint & 0xFF)); } + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + PutUnsafe(os, static_cast(codepoint & 0xFF)); + } + template static bool Decode(InputStream& is, unsigned* codepoint) { - unsigned char c = static_cast(is.Take()); + uint8_t c = static_cast(is.Take()); *codepoint = c; return c <= 0X7F; } template static bool Validate(InputStream& is, OutputStream& os) { - unsigned char c = is.Take(); - os.Put(c); + uint8_t c = static_cast(is.Take()); + os.Put(static_cast(c)); return c <= 0x7F; } template static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - Ch c = Take(is); - return c; + uint8_t c = static_cast(Take(is)); + return static_cast(c); } template static Ch Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - return is.Take(); + return static_cast(is.Take()); } template @@ -555,21 +620,28 @@ struct AutoUTF { #define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x template - RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) { + static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) { typedef void (*EncodeFunc)(OutputStream&, unsigned); static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; (*f[os.GetType()])(os, codepoint); } + template + static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; + (*f[os.GetType()])(os, codepoint); + } + template - RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { + static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) { typedef bool (*DecodeFunc)(InputStream&, unsigned*); static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; return (*f[is.GetType()])(is, codepoint); } template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { typedef bool (*ValidateFunc)(InputStream&, OutputStream&); static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; return (*f[is.GetType()])(is, os); @@ -586,7 +658,7 @@ template struct Transcoder { //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. template - RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { unsigned codepoint; if (!SourceEncoding::Decode(is, &codepoint)) return false; @@ -594,31 +666,50 @@ struct Transcoder { return true; } + template + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::EncodeUnsafe(os, codepoint); + return true; + } + //! Validate one Unicode codepoint from an encoded stream. template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { return Transcode(is, os); // Since source/target encoding is different, must transcode. } }; +// Forward declaration. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c); + //! Specialization of Transcoder with same source and target encoding. template struct Transcoder { template - RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. return true; } template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { return Encoding::Validate(is, os); // source/target encoding are the same } }; RAPIDJSON_NAMESPACE_END -#if defined(__GNUC__) || defined(_MSV_VER) +#if defined(__GNUC__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif diff --git a/contrib/rapidjson/include/rapidjson/error/en.h b/contrib/rapidjson/include/rapidjson/error/en.h index d5f9caab8..2db838bff 100644 --- a/contrib/rapidjson/include/rapidjson/error/en.h +++ b/contrib/rapidjson/include/rapidjson/error/en.h @@ -12,11 +12,17 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. -#ifndef RAPIDJSON_ERROR_EN_H__ -#define RAPIDJSON_ERROR_EN_H__ +#ifndef RAPIDJSON_ERROR_EN_H_ +#define RAPIDJSON_ERROR_EN_H_ #include "error.h" +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(covered-switch-default) +#endif + RAPIDJSON_NAMESPACE_BEGIN //! Maps error code of parsing into error message. @@ -32,7 +38,7 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); - case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not follow by other values."); + case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values."); case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); @@ -55,11 +61,14 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); - default: - return RAPIDJSON_ERROR_STRING("Unknown error."); + default: return RAPIDJSON_ERROR_STRING("Unknown error."); } } RAPIDJSON_NAMESPACE_END -#endif // RAPIDJSON_ERROR_EN_H__ +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_EN_H_ diff --git a/contrib/rapidjson/include/rapidjson/error/error.h b/contrib/rapidjson/include/rapidjson/error/error.h index f9094fb95..9311d2f03 100644 --- a/contrib/rapidjson/include/rapidjson/error/error.h +++ b/contrib/rapidjson/include/rapidjson/error/error.h @@ -12,11 +12,16 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. -#ifndef RAPIDJSON_ERROR_ERROR_H__ -#define RAPIDJSON_ERROR_ERROR_H__ +#ifndef RAPIDJSON_ERROR_ERROR_H_ +#define RAPIDJSON_ERROR_ERROR_H_ #include "../rapidjson.h" +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + /*! \file error.h */ /*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ @@ -99,7 +104,9 @@ enum ParseErrorCode { \see GenericReader::Parse, GenericDocument::Parse */ struct ParseResult { - + //!! Unspecified boolean type + typedef bool (ParseResult::*BooleanType)() const; +public: //! Default constructor, no error. ParseResult() : code_(kParseErrorNone), offset_(0) {} //! Constructor to set an error. @@ -110,8 +117,8 @@ struct ParseResult { //! Get the error offset, if \ref IsError(), 0 otherwise. size_t Offset() const { return offset_; } - //! Conversion to \c bool, returns \c true, iff !\ref IsError(). - operator bool() const { return !IsError(); } + //! Explicit conversion to \c bool, returns \c true, iff !\ref IsError(). + operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; } //! Whether the result is an error. bool IsError() const { return code_ != kParseErrorNone; } @@ -119,6 +126,10 @@ struct ParseResult { bool operator==(ParseErrorCode code) const { return code_ == code; } friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + bool operator!=(const ParseResult& that) const { return !(*this == that); } + bool operator!=(ParseErrorCode code) const { return !(*this == code); } + friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return err != code; } + //! Reset error code. void Clear() { Set(kParseErrorNone); } //! Update error code and offset. @@ -143,4 +154,8 @@ typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); RAPIDJSON_NAMESPACE_END -#endif // RAPIDJSON_ERROR_ERROR_H__ +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_ERROR_H_ diff --git a/contrib/rapidjson/include/rapidjson/filereadstream.h b/contrib/rapidjson/include/rapidjson/filereadstream.h index 3913eb74b..b56ea13b3 100644 --- a/contrib/rapidjson/include/rapidjson/filereadstream.h +++ b/contrib/rapidjson/include/rapidjson/filereadstream.h @@ -15,9 +15,16 @@ #ifndef RAPIDJSON_FILEREADSTREAM_H_ #define RAPIDJSON_FILEREADSTREAM_H_ -#include "rapidjson.h" +#include "stream.h" #include +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + RAPIDJSON_NAMESPACE_BEGIN //! File byte stream for input using fread(). @@ -85,4 +92,8 @@ private: RAPIDJSON_NAMESPACE_END +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + #endif // RAPIDJSON_FILESTREAM_H_ diff --git a/contrib/rapidjson/include/rapidjson/filewritestream.h b/contrib/rapidjson/include/rapidjson/filewritestream.h index dfb9cbd02..6378dd60e 100644 --- a/contrib/rapidjson/include/rapidjson/filewritestream.h +++ b/contrib/rapidjson/include/rapidjson/filewritestream.h @@ -15,9 +15,14 @@ #ifndef RAPIDJSON_FILEWRITESTREAM_H_ #define RAPIDJSON_FILEWRITESTREAM_H_ -#include "rapidjson.h" +#include "stream.h" #include +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +#endif + RAPIDJSON_NAMESPACE_BEGIN //! Wrapper of C file stream for input using fread(). @@ -57,7 +62,11 @@ public: void Flush() { if (current_ != buffer_) { - fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + size_t result = fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + if (result < static_cast(current_ - buffer_)) { + // failure deliberately ignored at this time + // added to avoid warn_unused_result build errors + } current_ = buffer_; } } @@ -88,4 +97,8 @@ inline void PutN(FileWriteStream& stream, char c, size_t n) { RAPIDJSON_NAMESPACE_END +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + #endif // RAPIDJSON_FILESTREAM_H_ diff --git a/contrib/rapidjson/include/rapidjson/fwd.h b/contrib/rapidjson/include/rapidjson/fwd.h new file mode 100644 index 000000000..e8104e841 --- /dev/null +++ b/contrib/rapidjson/include/rapidjson/fwd.h @@ -0,0 +1,151 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FWD_H_ +#define RAPIDJSON_FWD_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +// encodings.h + +template struct UTF8; +template struct UTF16; +template struct UTF16BE; +template struct UTF16LE; +template struct UTF32; +template struct UTF32BE; +template struct UTF32LE; +template struct ASCII; +template struct AutoUTF; + +template +struct Transcoder; + +// allocators.h + +class CrtAllocator; + +template +class MemoryPoolAllocator; + +// stream.h + +template +struct GenericStringStream; + +typedef GenericStringStream > StringStream; + +template +struct GenericInsituStringStream; + +typedef GenericInsituStringStream > InsituStringStream; + +// stringbuffer.h + +template +class GenericStringBuffer; + +typedef GenericStringBuffer, CrtAllocator> StringBuffer; + +// filereadstream.h + +class FileReadStream; + +// filewritestream.h + +class FileWriteStream; + +// memorybuffer.h + +template +struct GenericMemoryBuffer; + +typedef GenericMemoryBuffer MemoryBuffer; + +// memorystream.h + +struct MemoryStream; + +// reader.h + +template +struct BaseReaderHandler; + +template +class GenericReader; + +typedef GenericReader, UTF8, CrtAllocator> Reader; + +// writer.h + +template +class Writer; + +// prettywriter.h + +template +class PrettyWriter; + +// document.h + +template +struct GenericMember; + +template +class GenericMemberIterator; + +template +struct GenericStringRef; + +template +class GenericValue; + +typedef GenericValue, MemoryPoolAllocator > Value; + +template +class GenericDocument; + +typedef GenericDocument, MemoryPoolAllocator, CrtAllocator> Document; + +// pointer.h + +template +class GenericPointer; + +typedef GenericPointer Pointer; + +// schema.h + +template +class IGenericRemoteSchemaDocumentProvider; + +template +class GenericSchemaDocument; + +typedef GenericSchemaDocument SchemaDocument; +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +template < + typename SchemaDocumentType, + typename OutputHandler, + typename StateAllocator> +class GenericSchemaValidator; + +typedef GenericSchemaValidator, void>, CrtAllocator> SchemaValidator; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSONFWD_H_ diff --git a/contrib/rapidjson/include/rapidjson/internal/biginteger.h b/contrib/rapidjson/include/rapidjson/internal/biginteger.h index 99a30acf6..9d3e88c99 100644 --- a/contrib/rapidjson/include/rapidjson/internal/biginteger.h +++ b/contrib/rapidjson/include/rapidjson/internal/biginteger.h @@ -19,6 +19,7 @@ #if defined(_MSC_VER) && defined(_M_AMD64) #include // for _umul128 +#pragma intrinsic(_umul128) #endif RAPIDJSON_NAMESPACE_BEGIN @@ -50,7 +51,16 @@ public: if (length > 0) AppendDecimal64(decimals + i, decimals + i + length); } - + + BigInteger& operator=(const BigInteger &rhs) + { + if (this != &rhs) { + count_ = rhs.count_; + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + return *this; + } + BigInteger& operator=(uint64_t u) { digits_[0] = u; count_ = 1; @@ -230,7 +240,7 @@ private: uint64_t r = 0; for (const char* p = begin; p != end; ++p) { RAPIDJSON_ASSERT(*p >= '0' && *p <= '9'); - r = r * 10 + (*p - '0'); + r = r * 10u + static_cast(*p - '0'); } return r; } diff --git a/contrib/rapidjson/include/rapidjson/internal/diyfp.h b/contrib/rapidjson/include/rapidjson/internal/diyfp.h index 3b6c4238c..29abf8046 100644 --- a/contrib/rapidjson/include/rapidjson/internal/diyfp.h +++ b/contrib/rapidjson/include/rapidjson/internal/diyfp.h @@ -21,9 +21,10 @@ #include "../rapidjson.h" -#if defined(_MSC_VER) && defined(_M_AMD64) +#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER) #include #pragma intrinsic(_BitScanReverse64) +#pragma intrinsic(_umul128) #endif RAPIDJSON_NAMESPACE_BEGIN @@ -34,8 +35,13 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + struct DiyFp { - DiyFp() {} + DiyFp() : f(), e() {} DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} @@ -232,8 +238,8 @@ inline DiyFp GetCachedPower(int e, int* K) { } inline DiyFp GetCachedPower10(int exp, int *outExp) { - unsigned index = (exp + 348) / 8; - *outExp = -348 + index * 8; + unsigned index = (static_cast(exp) + 348u) / 8u; + *outExp = -348 + static_cast(index) * 8; return GetCachedPowerByIndex(index); } @@ -241,6 +247,11 @@ inline DiyFp GetCachedPower10(int exp, int *outExp) { RAPIDJSON_DIAG_POP #endif +#ifdef __clang__ +RAPIDJSON_DIAG_POP +RAPIDJSON_DIAG_OFF(padded) +#endif + } // namespace internal RAPIDJSON_NAMESPACE_END diff --git a/contrib/rapidjson/include/rapidjson/internal/dtoa.h b/contrib/rapidjson/include/rapidjson/internal/dtoa.h index 2d8d2e46a..bf2e9b2e5 100644 --- a/contrib/rapidjson/include/rapidjson/internal/dtoa.h +++ b/contrib/rapidjson/include/rapidjson/internal/dtoa.h @@ -29,6 +29,7 @@ namespace internal { #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124 #endif inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { @@ -40,7 +41,7 @@ inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uin } } -inline unsigned CountDecimalDigit32(uint32_t n) { +inline int CountDecimalDigit32(uint32_t n) { // Simple pure C++ implementation was faster than __builtin_clz version in this situation. if (n < 10) return 1; if (n < 100) return 2; @@ -101,7 +102,8 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff kappa--; if (p2 < delta) { *K += kappa; - GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-kappa]); + int index = -kappa; + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[index] : 0)); return; } } @@ -145,10 +147,10 @@ inline char* WriteExponent(int K, char* buffer) { return buffer; } -inline char* Prettify(char* buffer, int length, int k) { +inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { const int kk = length + k; // 10^(kk-1) <= v < 10^kk - if (length <= kk && kk <= 21) { + if (0 <= k && kk <= 21) { // 1234e7 -> 12340000000 for (int i = length; i < kk; i++) buffer[i] = '0'; @@ -158,19 +160,44 @@ inline char* Prettify(char* buffer, int length, int k) { } else if (0 < kk && kk <= 21) { // 1234e-2 -> 12.34 - std::memmove(&buffer[kk + 1], &buffer[kk], length - kk); + std::memmove(&buffer[kk + 1], &buffer[kk], static_cast(length - kk)); buffer[kk] = '.'; - return &buffer[length + 1]; + if (0 > k + maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = kk + maxDecimalPlaces; i > kk + 1; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[kk + 2]; // Reserve one zero + } + else + return &buffer[length + 1]; } else if (-6 < kk && kk <= 0) { // 1234e-6 -> 0.001234 const int offset = 2 - kk; - std::memmove(&buffer[offset], &buffer[0], length); + std::memmove(&buffer[offset], &buffer[0], static_cast(length)); buffer[0] = '0'; buffer[1] = '.'; for (int i = 2; i < offset; i++) buffer[i] = '0'; - return &buffer[length + offset]; + if (length - kk > maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = maxDecimalPlaces + 1; i > 2; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[3]; // Reserve one zero + } + else + return &buffer[length + offset]; + } + else if (kk < -maxDecimalPlaces) { + // Truncate to zero + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; } else if (length == 1) { // 1e30 @@ -179,14 +206,15 @@ inline char* Prettify(char* buffer, int length, int k) { } else { // 1234e30 -> 1.234e33 - std::memmove(&buffer[2], &buffer[1], length - 1); + std::memmove(&buffer[2], &buffer[1], static_cast(length - 1)); buffer[1] = '.'; buffer[length + 1] = 'e'; return WriteExponent(kk - 1, &buffer[0 + length + 2]); } } -inline char* dtoa(double value, char* buffer) { +inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) { + RAPIDJSON_ASSERT(maxDecimalPlaces >= 1); Double d(value); if (d.IsZero()) { if (d.Sign()) @@ -203,7 +231,7 @@ inline char* dtoa(double value, char* buffer) { } int length, K; Grisu2(value, buffer, &length, &K); - return Prettify(buffer, length, K); + return Prettify(buffer, length, K, maxDecimalPlaces); } } diff --git a/contrib/rapidjson/include/rapidjson/internal/ieee754.h b/contrib/rapidjson/include/rapidjson/internal/ieee754.h index e3f03364c..c2684ba2a 100644 --- a/contrib/rapidjson/include/rapidjson/internal/ieee754.h +++ b/contrib/rapidjson/include/rapidjson/internal/ieee754.h @@ -40,6 +40,7 @@ public: bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; } bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; } + bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; } bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; } bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; } @@ -47,7 +48,7 @@ public: int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } - static unsigned EffectiveSignificandSize(int order) { + static int EffectiveSignificandSize(int order) { if (order >= -1021) return 53; else if (order <= -1074) diff --git a/contrib/rapidjson/include/rapidjson/internal/regex.h b/contrib/rapidjson/include/rapidjson/internal/regex.h new file mode 100644 index 000000000..e1a2faae5 --- /dev/null +++ b/contrib/rapidjson/include/rapidjson/internal/regex.h @@ -0,0 +1,734 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_REGEX_H_ +#define RAPIDJSON_INTERNAL_REGEX_H_ + +#include "../allocators.h" +#include "../stream.h" +#include "stack.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(implicit-fallthrough) +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#if __GNUC__ >= 7 +RAPIDJSON_DIAG_OFF(implicit-fallthrough) +#endif +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +#ifndef RAPIDJSON_REGEX_VERBOSE +#define RAPIDJSON_REGEX_VERBOSE 0 +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// DecodedStream + +template +class DecodedStream { +public: + DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } + unsigned Peek() { return codepoint_; } + unsigned Take() { + unsigned c = codepoint_; + if (c) // No further decoding when '\0' + Decode(); + return c; + } + +private: + void Decode() { + if (!Encoding::Decode(ss_, &codepoint_)) + codepoint_ = 0; + } + + SourceStream& ss_; + unsigned codepoint_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericRegex + +static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 +static const SizeType kRegexInvalidRange = ~SizeType(0); + +template +class GenericRegexSearch; + +//! Regular expression engine with subset of ECMAscript grammar. +/*! + Supported regular expression syntax: + - \c ab Concatenation + - \c a|b Alternation + - \c a? Zero or one + - \c a* Zero or more + - \c a+ One or more + - \c a{3} Exactly 3 times + - \c a{3,} At least 3 times + - \c a{3,5} 3 to 5 times + - \c (ab) Grouping + - \c ^a At the beginning + - \c a$ At the end + - \c . Any character + - \c [abc] Character classes + - \c [a-c] Character class range + - \c [a-z0-9_] Character class combination + - \c [^abc] Negated character classes + - \c [^a-c] Negated character class range + - \c [\b] Backspace (U+0008) + - \c \\| \\\\ ... Escape characters + - \c \\f Form feed (U+000C) + - \c \\n Line feed (U+000A) + - \c \\r Carriage return (U+000D) + - \c \\t Tab (U+0009) + - \c \\v Vertical tab (U+000B) + + \note This is a Thompson NFA engine, implemented with reference to + Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).", + https://swtch.com/~rsc/regexp/regexp1.html +*/ +template +class GenericRegex { +public: + typedef Encoding EncodingType; + typedef typename Encoding::Ch Ch; + template friend class GenericRegexSearch; + + GenericRegex(const Ch* source, Allocator* allocator = 0) : + states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), + anchorBegin_(), anchorEnd_() + { + GenericStringStream ss(source); + DecodedStream, Encoding> ds(ss); + Parse(ds); + } + + ~GenericRegex() {} + + bool IsValid() const { + return root_ != kRegexInvalidState; + } + +private: + enum Operator { + kZeroOrOne, + kZeroOrMore, + kOneOrMore, + kConcatenation, + kAlternation, + kLeftParenthesis + }; + + static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.' + static const unsigned kRangeCharacterClass = 0xFFFFFFFE; + static const unsigned kRangeNegationFlag = 0x80000000; + + struct Range { + unsigned start; // + unsigned end; + SizeType next; + }; + + struct State { + SizeType out; //!< Equals to kInvalid for matching state + SizeType out1; //!< Equals to non-kInvalid for split + SizeType rangeStart; + unsigned codepoint; + }; + + struct Frag { + Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {} + SizeType start; + SizeType out; //!< link-list of all output states + SizeType minIndex; + }; + + State& GetState(SizeType index) { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + const State& GetState(SizeType index) const { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + Range& GetRange(SizeType index) { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + const Range& GetRange(SizeType index) const { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + template + void Parse(DecodedStream& ds) { + Allocator allocator; + Stack operandStack(&allocator, 256); // Frag + Stack operatorStack(&allocator, 256); // Operator + Stack atomCountStack(&allocator, 256); // unsigned (Atom per parenthesis) + + *atomCountStack.template Push() = 0; + + unsigned codepoint; + while (ds.Peek() != 0) { + switch (codepoint = ds.Take()) { + case '^': + anchorBegin_ = true; + break; + + case '$': + anchorEnd_ = true; + break; + + case '|': + while (!operatorStack.Empty() && *operatorStack.template Top() < kAlternation) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + *operatorStack.template Push() = kAlternation; + *atomCountStack.template Top() = 0; + break; + + case '(': + *operatorStack.template Push() = kLeftParenthesis; + *atomCountStack.template Push() = 0; + break; + + case ')': + while (!operatorStack.Empty() && *operatorStack.template Top() != kLeftParenthesis) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + if (operatorStack.Empty()) + return; + operatorStack.template Pop(1); + atomCountStack.template Pop(1); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '?': + if (!Eval(operandStack, kZeroOrOne)) + return; + break; + + case '*': + if (!Eval(operandStack, kZeroOrMore)) + return; + break; + + case '+': + if (!Eval(operandStack, kOneOrMore)) + return; + break; + + case '{': + { + unsigned n, m; + if (!ParseUnsigned(ds, &n)) + return; + + if (ds.Peek() == ',') { + ds.Take(); + if (ds.Peek() == '}') + m = kInfinityQuantifier; + else if (!ParseUnsigned(ds, &m) || m < n) + return; + } + else + m = n; + + if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}') + return; + ds.Take(); + } + break; + + case '.': + PushOperand(operandStack, kAnyCharacterClass); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '[': + { + SizeType range; + if (!ParseRange(ds, &range)) + return; + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass); + GetState(s).rangeStart = range; + *operandStack.template Push() = Frag(s, s, s); + } + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '\\': // Escape character + if (!CharacterEscape(ds, &codepoint)) + return; // Unsupported escape character + // fall through to default + + default: // Pattern character + PushOperand(operandStack, codepoint); + ImplicitConcatenation(atomCountStack, operatorStack); + } + } + + while (!operatorStack.Empty()) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + + // Link the operand to matching state. + if (operandStack.GetSize() == sizeof(Frag)) { + Frag* e = operandStack.template Pop(1); + Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0)); + root_ = e->start; + +#if RAPIDJSON_REGEX_VERBOSE + printf("root: %d\n", root_); + for (SizeType i = 0; i < stateCount_ ; i++) { + State& s = GetState(i); + printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint); + } + printf("\n"); +#endif + } + } + + SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { + State* s = states_.template Push(); + s->out = out; + s->out1 = out1; + s->codepoint = codepoint; + s->rangeStart = kRegexInvalidRange; + return stateCount_++; + } + + void PushOperand(Stack& operandStack, unsigned codepoint) { + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint); + *operandStack.template Push() = Frag(s, s, s); + } + + void ImplicitConcatenation(Stack& atomCountStack, Stack& operatorStack) { + if (*atomCountStack.template Top()) + *operatorStack.template Push() = kConcatenation; + (*atomCountStack.template Top())++; + } + + SizeType Append(SizeType l1, SizeType l2) { + SizeType old = l1; + while (GetState(l1).out != kRegexInvalidState) + l1 = GetState(l1).out; + GetState(l1).out = l2; + return old; + } + + void Patch(SizeType l, SizeType s) { + for (SizeType next; l != kRegexInvalidState; l = next) { + next = GetState(l).out; + GetState(l).out = s; + } + } + + bool Eval(Stack& operandStack, Operator op) { + switch (op) { + case kConcatenation: + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2); + { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + Patch(e1.out, e2.start); + *operandStack.template Push() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex)); + } + return true; + + case kAlternation: + if (operandStack.GetSize() >= sizeof(Frag) * 2) { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + SizeType s = NewState(e1.start, e2.start, 0); + *operandStack.template Push() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex)); + return true; + } + return false; + + case kZeroOrOne: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + *operandStack.template Push() = Frag(s, Append(e.out, s), e.minIndex); + return true; + } + return false; + + case kZeroOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(s, s, e.minIndex); + return true; + } + return false; + + default: + RAPIDJSON_ASSERT(op == kOneOrMore); + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(e.start, s, e.minIndex); + return true; + } + return false; + } + } + + bool EvalQuantifier(Stack& operandStack, unsigned n, unsigned m) { + RAPIDJSON_ASSERT(n <= m); + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag)); + + if (n == 0) { + if (m == 0) // a{0} not support + return false; + else if (m == kInfinityQuantifier) + Eval(operandStack, kZeroOrMore); // a{0,} -> a* + else { + Eval(operandStack, kZeroOrOne); // a{0,5} -> a? + for (unsigned i = 0; i < m - 1; i++) + CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a? + for (unsigned i = 0; i < m - 1; i++) + Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a? + } + return true; + } + + for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a + CloneTopOperand(operandStack); + + if (m == kInfinityQuantifier) + Eval(operandStack, kOneOrMore); // a{3,} -> a a a+ + else if (m > n) { + CloneTopOperand(operandStack); // a{3,5} -> a a a a + Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a? + for (unsigned i = n; i < m - 1; i++) + CloneTopOperand(operandStack); // a{3,5} -> a a a a? a? + for (unsigned i = n; i < m; i++) + Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a? + } + + for (unsigned i = 0; i < n - 1; i++) + Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a? + + return true; + } + + static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; } + + void CloneTopOperand(Stack& operandStack) { + const Frag src = *operandStack.template Top(); // Copy constructor to prevent invalidation + SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_) + State* s = states_.template Push(count); + memcpy(s, &GetState(src.minIndex), count * sizeof(State)); + for (SizeType j = 0; j < count; j++) { + if (s[j].out != kRegexInvalidState) + s[j].out += count; + if (s[j].out1 != kRegexInvalidState) + s[j].out1 += count; + } + *operandStack.template Push() = Frag(src.start + count, src.out + count, src.minIndex + count); + stateCount_ += count; + } + + template + bool ParseUnsigned(DecodedStream& ds, unsigned* u) { + unsigned r = 0; + if (ds.Peek() < '0' || ds.Peek() > '9') + return false; + while (ds.Peek() >= '0' && ds.Peek() <= '9') { + if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295 + return false; // overflow + r = r * 10 + (ds.Take() - '0'); + } + *u = r; + return true; + } + + template + bool ParseRange(DecodedStream& ds, SizeType* range) { + bool isBegin = true; + bool negate = false; + int step = 0; + SizeType start = kRegexInvalidRange; + SizeType current = kRegexInvalidRange; + unsigned codepoint; + while ((codepoint = ds.Take()) != 0) { + if (isBegin) { + isBegin = false; + if (codepoint == '^') { + negate = true; + continue; + } + } + + switch (codepoint) { + case ']': + if (start == kRegexInvalidRange) + return false; // Error: nothing inside [] + if (step == 2) { // Add trailing '-' + SizeType r = NewRange('-'); + RAPIDJSON_ASSERT(current != kRegexInvalidRange); + GetRange(current).next = r; + } + if (negate) + GetRange(start).start |= kRangeNegationFlag; + *range = start; + return true; + + case '\\': + if (ds.Peek() == 'b') { + ds.Take(); + codepoint = 0x0008; // Escape backspace character + } + else if (!CharacterEscape(ds, &codepoint)) + return false; + // fall through to default + + default: + switch (step) { + case 1: + if (codepoint == '-') { + step++; + break; + } + // fall through to step 0 for other characters + + case 0: + { + SizeType r = NewRange(codepoint); + if (current != kRegexInvalidRange) + GetRange(current).next = r; + if (start == kRegexInvalidRange) + start = r; + current = r; + } + step = 1; + break; + + default: + RAPIDJSON_ASSERT(step == 2); + GetRange(current).end = codepoint; + step = 0; + } + } + } + return false; + } + + SizeType NewRange(unsigned codepoint) { + Range* r = ranges_.template Push(); + r->start = r->end = codepoint; + r->next = kRegexInvalidRange; + return rangeCount_++; + } + + template + bool CharacterEscape(DecodedStream& ds, unsigned* escapedCodepoint) { + unsigned codepoint; + switch (codepoint = ds.Take()) { + case '^': + case '$': + case '|': + case '(': + case ')': + case '?': + case '*': + case '+': + case '.': + case '[': + case ']': + case '{': + case '}': + case '\\': + *escapedCodepoint = codepoint; return true; + case 'f': *escapedCodepoint = 0x000C; return true; + case 'n': *escapedCodepoint = 0x000A; return true; + case 'r': *escapedCodepoint = 0x000D; return true; + case 't': *escapedCodepoint = 0x0009; return true; + case 'v': *escapedCodepoint = 0x000B; return true; + default: + return false; // Unsupported escape character + } + } + + Stack states_; + Stack ranges_; + SizeType root_; + SizeType stateCount_; + SizeType rangeCount_; + + static const unsigned kInfinityQuantifier = ~0u; + + // For SearchWithAnchoring() + bool anchorBegin_; + bool anchorEnd_; +}; + +template +class GenericRegexSearch { +public: + typedef typename RegexType::EncodingType Encoding; + typedef typename Encoding::Ch Ch; + + GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) : + regex_(regex), allocator_(allocator), ownAllocator_(0), + state0_(allocator, 0), state1_(allocator, 0), stateSet_() + { + RAPIDJSON_ASSERT(regex_.IsValid()); + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + stateSet_ = static_cast(allocator_->Malloc(GetStateSetSize())); + state0_.template Reserve(regex_.stateCount_); + state1_.template Reserve(regex_.stateCount_); + } + + ~GenericRegexSearch() { + Allocator::Free(stateSet_); + RAPIDJSON_DELETE(ownAllocator_); + } + + template + bool Match(InputStream& is) { + return SearchWithAnchoring(is, true, true); + } + + bool Match(const Ch* s) { + GenericStringStream is(s); + return Match(is); + } + + template + bool Search(InputStream& is) { + return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_); + } + + bool Search(const Ch* s) { + GenericStringStream is(s); + return Search(is); + } + +private: + typedef typename RegexType::State State; + typedef typename RegexType::Range Range; + + template + bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) { + DecodedStream ds(is); + + state0_.Clear(); + Stack *current = &state0_, *next = &state1_; + const size_t stateSetSize = GetStateSetSize(); + std::memset(stateSet_, 0, stateSetSize); + + bool matched = AddState(*current, regex_.root_); + unsigned codepoint; + while (!current->Empty() && (codepoint = ds.Take()) != 0) { + std::memset(stateSet_, 0, stateSetSize); + next->Clear(); + matched = false; + for (const SizeType* s = current->template Bottom(); s != current->template End(); ++s) { + const State& sr = regex_.GetState(*s); + if (sr.codepoint == codepoint || + sr.codepoint == RegexType::kAnyCharacterClass || + (sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) + { + matched = AddState(*next, sr.out) || matched; + if (!anchorEnd && matched) + return true; + } + if (!anchorBegin) + AddState(*next, regex_.root_); + } + internal::Swap(current, next); + } + + return matched; + } + + size_t GetStateSetSize() const { + return (regex_.stateCount_ + 31) / 32 * 4; + } + + // Return whether the added states is a match state + bool AddState(Stack& l, SizeType index) { + RAPIDJSON_ASSERT(index != kRegexInvalidState); + + const State& s = regex_.GetState(index); + if (s.out1 != kRegexInvalidState) { // Split + bool matched = AddState(l, s.out); + return AddState(l, s.out1) || matched; + } + else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) { + stateSet_[index >> 5] |= (1u << (index & 31)); + *l.template PushUnsafe() = index; + } + return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. + } + + bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { + bool yes = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0; + while (rangeIndex != kRegexInvalidRange) { + const Range& r = regex_.GetRange(rangeIndex); + if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end) + return yes; + rangeIndex = r.next; + } + return !yes; + } + + const RegexType& regex_; + Allocator* allocator_; + Allocator* ownAllocator_; + Stack state0_; + Stack state1_; + uint32_t* stateSet_; +}; + +typedef GenericRegex > Regex; +typedef GenericRegexSearch RegexSearch; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_REGEX_H_ diff --git a/contrib/rapidjson/include/rapidjson/internal/stack.h b/contrib/rapidjson/include/rapidjson/internal/stack.h index 722d56923..5c5398c35 100644 --- a/contrib/rapidjson/include/rapidjson/internal/stack.h +++ b/contrib/rapidjson/include/rapidjson/internal/stack.h @@ -15,7 +15,13 @@ #ifndef RAPIDJSON_INTERNAL_STACK_H_ #define RAPIDJSON_INTERNAL_STACK_H_ -#include "../rapidjson.h" +#include "../allocators.h" +#include "swap.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif RAPIDJSON_NAMESPACE_BEGIN namespace internal { @@ -32,7 +38,6 @@ public: // Optimization note: Do not allocate memory for stack_ in constructor. // Do it lazily when first Push() -> Expand() -> Resize(). Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { - RAPIDJSON_ASSERT(stackCapacity > 0); } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS @@ -81,6 +86,15 @@ public: } #endif + void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(stack_, rhs.stack_); + internal::Swap(stackTop_, rhs.stackTop_); + internal::Swap(stackEnd_, rhs.stackEnd_); + internal::Swap(initialCapacity_, rhs.initialCapacity_); + } + void Clear() { stackTop_ = stack_; } void ShrinkToFit() { @@ -98,11 +112,22 @@ public: // Optimization note: try to minimize the size of this function for force inline. // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. template - RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { + RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { // Expand the stack if needed - if (stackTop_ + sizeof(T) * count >= stackEnd_) + if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_)) Expand(count); + } + template + RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { + Reserve(count); + return PushUnsafe(count); + } + + template + RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { + RAPIDJSON_ASSERT(stackTop_); + RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_); T* ret = reinterpret_cast(stackTop_); stackTop_ += sizeof(T) * count; return ret; @@ -122,9 +147,32 @@ public: } template - T* Bottom() { return (T*)stack_; } + const T* Top() const { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + T* End() { return reinterpret_cast(stackTop_); } + + template + const T* End() const { return reinterpret_cast(stackTop_); } + + template + T* Bottom() { return reinterpret_cast(stack_); } + + template + const T* Bottom() const { return reinterpret_cast(stack_); } + + bool HasAllocator() const { + return allocator_ != 0; + } + + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } - Allocator& GetAllocator() { return *allocator_; } bool Empty() const { return stackTop_ == stack_; } size_t GetSize() const { return static_cast(stackTop_ - stack_); } size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } @@ -136,7 +184,7 @@ private: size_t newCapacity; if (stack_ == 0) { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); newCapacity = initialCapacity_; } else { newCapacity = GetCapacity(); @@ -151,7 +199,7 @@ private: void Resize(size_t newCapacity) { const size_t size = GetSize(); // Backup the current size - stack_ = (char*)allocator_->Realloc(stack_, GetCapacity(), newCapacity); + stack_ = static_cast(allocator_->Realloc(stack_, GetCapacity(), newCapacity)); stackTop_ = stack_ + size; stackEnd_ = stack_ + newCapacity; } @@ -176,4 +224,8 @@ private: } // namespace internal RAPIDJSON_NAMESPACE_END +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + #endif // RAPIDJSON_STACK_H_ diff --git a/contrib/rapidjson/include/rapidjson/internal/strfunc.h b/contrib/rapidjson/include/rapidjson/internal/strfunc.h index 84405065a..226439a76 100644 --- a/contrib/rapidjson/include/rapidjson/internal/strfunc.h +++ b/contrib/rapidjson/include/rapidjson/internal/strfunc.h @@ -15,7 +15,8 @@ #ifndef RAPIDJSON_INTERNAL_STRFUNC_H_ #define RAPIDJSON_INTERNAL_STRFUNC_H_ -#include "../rapidjson.h" +#include "../stream.h" +#include RAPIDJSON_NAMESPACE_BEGIN namespace internal { @@ -28,11 +29,40 @@ namespace internal { */ template inline SizeType StrLen(const Ch* s) { + RAPIDJSON_ASSERT(s != 0); const Ch* p = s; while (*p) ++p; return SizeType(p - s); } +template <> +inline SizeType StrLen(const char* s) { + return SizeType(std::strlen(s)); +} + +template <> +inline SizeType StrLen(const wchar_t* s) { + return SizeType(std::wcslen(s)); +} + +//! Returns number of code points in a encoded string. +template +bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { + RAPIDJSON_ASSERT(s != 0); + RAPIDJSON_ASSERT(outCount != 0); + GenericStringStream is(s); + const typename Encoding::Ch* end = s + length; + SizeType count = 0; + while (is.src_ < end) { + unsigned codepoint; + if (!Encoding::Decode(is, &codepoint)) + return false; + count++; + } + *outCount = count; + return true; +} + } // namespace internal RAPIDJSON_NAMESPACE_END diff --git a/contrib/rapidjson/include/rapidjson/internal/strtod.h b/contrib/rapidjson/include/rapidjson/internal/strtod.h index ace65f677..adf49e349 100644 --- a/contrib/rapidjson/include/rapidjson/internal/strtod.h +++ b/contrib/rapidjson/include/rapidjson/internal/strtod.h @@ -15,7 +15,6 @@ #ifndef RAPIDJSON_STRTOD_ #define RAPIDJSON_STRTOD_ -#include "../rapidjson.h" #include "ieee754.h" #include "biginteger.h" #include "diyfp.h" @@ -95,13 +94,13 @@ inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { hS_Exp2 -= common_Exp2; BigInteger dS = d; - dS.MultiplyPow5(dS_Exp5) <<= dS_Exp2; + dS.MultiplyPow5(static_cast(dS_Exp5)) <<= static_cast(dS_Exp2); BigInteger bS(bInt); - bS.MultiplyPow5(bS_Exp5) <<= bS_Exp2; + bS.MultiplyPow5(static_cast(bS_Exp5)) <<= static_cast(bS_Exp2); BigInteger hS(1); - hS.MultiplyPow5(hS_Exp5) <<= hS_Exp2; + hS.MultiplyPow5(static_cast(hS_Exp5)) <<= static_cast(hS_Exp2); BigInteger delta(0); dS.Difference(bS, &delta); @@ -134,22 +133,22 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) break; - significand = significand * 10 + (decimals[i] - '0'); + significand = significand * 10u + static_cast(decimals[i] - '0'); } if (i < length && decimals[i] >= '5') // Rounding significand++; size_t remaining = length - i; - const unsigned kUlpShift = 3; - const unsigned kUlp = 1 << kUlpShift; - int error = (remaining == 0) ? 0 : kUlp / 2; + const int kUlpShift = 3; + const int kUlp = 1 << kUlpShift; + int64_t error = (remaining == 0) ? 0 : kUlp / 2; DiyFp v(significand, 0); v = v.Normalize(); error <<= -v.e; - const int dExp = (int)decimalPosition - (int)i + exp; + const int dExp = static_cast(decimalPosition) - static_cast(i) + exp; int actualExp; DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); @@ -163,10 +162,10 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6 DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7 }; - int adjustment = dExp - actualExp - 1; + int adjustment = dExp - actualExp - 1; RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7); v = v * kPow10[adjustment]; - if (length + adjustment > 19) // has more digits than decimal digits in 64-bit + if (length + static_cast(adjustment)> 19u) // has more digits than decimal digits in 64-bit error += kUlp / 2; } @@ -178,10 +177,10 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit v = v.Normalize(); error <<= oldExp - v.e; - const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); - unsigned precisionSize = 64 - effectiveSignificandSize; + const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + int precisionSize = 64 - effectiveSignificandSize; if (precisionSize + kUlpShift >= 64) { - unsigned scaleExp = (precisionSize + kUlpShift) - 63; + int scaleExp = (precisionSize + kUlpShift) - 63; v.f >>= scaleExp; v.e += scaleExp; error = (error >> scaleExp) + 1 + kUlp; @@ -191,7 +190,7 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; - if (precisionBits >= halfWay + error) { + if (precisionBits >= halfWay + static_cast(error)) { rounded.f++; if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340) rounded.f >>= 1; @@ -201,12 +200,12 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit *result = rounded.ToDouble(); - return halfWay - error >= precisionBits || precisionBits >= halfWay + error; + return halfWay - static_cast(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); } inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) { const BigInteger dInt(decimals, length); - const int dExp = (int)decimalPosition - (int)length + exp; + const int dExp = static_cast(decimalPosition) - static_cast(length) + exp; Double a(approx); int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); if (cmp < 0) @@ -246,10 +245,10 @@ inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t // Trim right-most digits const int kMaxDecimalDigit = 780; - if ((int)length > kMaxDecimalDigit) { - int delta = (int(length) - kMaxDecimalDigit); + if (static_cast(length) > kMaxDecimalDigit) { + int delta = (static_cast(length) - kMaxDecimalDigit); exp += delta; - decimalPosition -= delta; + decimalPosition -= static_cast(delta); length = kMaxDecimalDigit; } diff --git a/contrib/rapidjson/include/rapidjson/internal/swap.h b/contrib/rapidjson/include/rapidjson/internal/swap.h index 0590921f1..666e49f97 100644 --- a/contrib/rapidjson/include/rapidjson/internal/swap.h +++ b/contrib/rapidjson/include/rapidjson/internal/swap.h @@ -17,6 +17,11 @@ #include "../rapidjson.h" +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + RAPIDJSON_NAMESPACE_BEGIN namespace internal { @@ -34,4 +39,8 @@ inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT { } // namespace internal RAPIDJSON_NAMESPACE_END +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + #endif // RAPIDJSON_INTERNAL_SWAP_H_ diff --git a/contrib/rapidjson/include/rapidjson/istreamwrapper.h b/contrib/rapidjson/include/rapidjson/istreamwrapper.h new file mode 100644 index 000000000..8639c8c3c --- /dev/null +++ b/contrib/rapidjson/include/rapidjson/istreamwrapper.h @@ -0,0 +1,115 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ISTREAMWRAPPER_H_ +#define RAPIDJSON_ISTREAMWRAPPER_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::istringstream + - \c std::stringstream + - \c std::wistringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wifstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_istream. +*/ + +template +class BasicIStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + BasicIStreamWrapper(StreamType& stream) : stream_(stream), count_(), peekBuffer_() {} + + Ch Peek() const { + typename StreamType::int_type c = stream_.peek(); + return RAPIDJSON_LIKELY(c != StreamType::traits_type::eof()) ? static_cast(c) : static_cast('\0'); + } + + Ch Take() { + typename StreamType::int_type c = stream_.get(); + if (RAPIDJSON_LIKELY(c != StreamType::traits_type::eof())) { + count_++; + return static_cast(c); + } + else + return '\0'; + } + + // tellg() may return -1 when failed. So we count by ourself. + size_t Tell() const { return count_; } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + RAPIDJSON_ASSERT(sizeof(Ch) == 1); // Only usable for byte stream. + int i; + bool hasError = false; + for (i = 0; i < 4; ++i) { + typename StreamType::int_type c = stream_.get(); + if (c == StreamType::traits_type::eof()) { + hasError = true; + stream_.clear(); + break; + } + peekBuffer_[i] = static_cast(c); + } + for (--i; i >= 0; --i) + stream_.putback(peekBuffer_[i]); + return !hasError ? peekBuffer_ : 0; + } + +private: + BasicIStreamWrapper(const BasicIStreamWrapper&); + BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); + + StreamType& stream_; + size_t count_; //!< Number of characters read. Note: + mutable Ch peekBuffer_[4]; +}; + +typedef BasicIStreamWrapper IStreamWrapper; +typedef BasicIStreamWrapper WIStreamWrapper; + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ISTREAMWRAPPER_H_ diff --git a/contrib/rapidjson/include/rapidjson/memorybuffer.h b/contrib/rapidjson/include/rapidjson/memorybuffer.h index 2484b2185..39bee1dec 100644 --- a/contrib/rapidjson/include/rapidjson/memorybuffer.h +++ b/contrib/rapidjson/include/rapidjson/memorybuffer.h @@ -15,7 +15,7 @@ #ifndef RAPIDJSON_MEMORYBUFFER_H_ #define RAPIDJSON_MEMORYBUFFER_H_ -#include "rapidjson.h" +#include "stream.h" #include "internal/stack.h" RAPIDJSON_NAMESPACE_BEGIN diff --git a/contrib/rapidjson/include/rapidjson/memorystream.h b/contrib/rapidjson/include/rapidjson/memorystream.h index 99feae5d7..1d71d8a4f 100644 --- a/contrib/rapidjson/include/rapidjson/memorystream.h +++ b/contrib/rapidjson/include/rapidjson/memorystream.h @@ -15,7 +15,13 @@ #ifndef RAPIDJSON_MEMORYSTREAM_H_ #define RAPIDJSON_MEMORYSTREAM_H_ -#include "rapidjson.h" +#include "stream.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif RAPIDJSON_NAMESPACE_BEGIN @@ -36,8 +42,8 @@ struct MemoryStream { MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} - Ch Peek() const { return (src_ == end_) ? '\0' : *src_; } - Ch Take() { return (src_ == end_) ? '\0' : *src_++; } + Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; } + Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; } size_t Tell() const { return static_cast(src_ - begin_); } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } @@ -58,4 +64,8 @@ struct MemoryStream { RAPIDJSON_NAMESPACE_END +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + #endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/contrib/rapidjson/include/rapidjson/msinttypes/stdint.h b/contrib/rapidjson/include/rapidjson/msinttypes/stdint.h index a26fff4bf..3d4477b9a 100644 --- a/contrib/rapidjson/include/rapidjson/msinttypes/stdint.h +++ b/contrib/rapidjson/include/rapidjson/msinttypes/stdint.h @@ -89,14 +89,14 @@ #include // For Visual Studio 6 in C++ mode and for many Visual Studio versions when -// compiling for ARM we should wrap include with 'extern "C++" {}' -// or compiler give many errors like this: +// compiling for ARM we have to wrap include with 'extern "C++" {}' +// or compiler would give many errors like this: // error C2733: second C linkage of overloaded function 'wmemchr' not allowed -#ifdef __cplusplus +#if defined(__cplusplus) && !defined(_M_ARM) extern "C" { #endif # include -#ifdef __cplusplus +#if defined(__cplusplus) && !defined(_M_ARM) } #endif diff --git a/contrib/rapidjson/include/rapidjson/ostreamwrapper.h b/contrib/rapidjson/include/rapidjson/ostreamwrapper.h new file mode 100644 index 000000000..6f4667c08 --- /dev/null +++ b/contrib/rapidjson/include/rapidjson/ostreamwrapper.h @@ -0,0 +1,81 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_OSTREAMWRAPPER_H_ +#define RAPIDJSON_OSTREAMWRAPPER_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::ostringstream + - \c std::stringstream + - \c std::wpstringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wofstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_ostream. +*/ + +template +class BasicOStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + BasicOStreamWrapper(StreamType& stream) : stream_(stream) {} + + void Put(Ch c) { + stream_.put(c); + } + + void Flush() { + stream_.flush(); + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + BasicOStreamWrapper(const BasicOStreamWrapper&); + BasicOStreamWrapper& operator=(const BasicOStreamWrapper&); + + StreamType& stream_; +}; + +typedef BasicOStreamWrapper OStreamWrapper; +typedef BasicOStreamWrapper WOStreamWrapper; + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_OSTREAMWRAPPER_H_ diff --git a/contrib/rapidjson/include/rapidjson/pointer.h b/contrib/rapidjson/include/rapidjson/pointer.h index 5d2aa8d63..0f377efec 100644 --- a/contrib/rapidjson/include/rapidjson/pointer.h +++ b/contrib/rapidjson/include/rapidjson/pointer.h @@ -18,6 +18,16 @@ #include "document.h" #include "internal/itoa.h" +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + RAPIDJSON_NAMESPACE_BEGIN static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token @@ -71,7 +81,7 @@ template class GenericPointer { public: typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value - typedef typename EncodingType::Ch Ch; //!< Character type from Value + typedef typename ValueType::Ch Ch; //!< Character type from Value //! A token is the basic units of internal representation. /*! @@ -96,7 +106,7 @@ public: //@{ //! Default constructor. - GenericPointer() : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} //! Constructor that parses a string or URI fragment representation. /*! @@ -155,7 +165,7 @@ public: GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} //! Copy constructor. - GenericPointer(const GenericPointer& rhs) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + GenericPointer(const GenericPointer& rhs, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { *this = rhs; } @@ -230,7 +240,7 @@ public: template RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) Append(T* name, Allocator* allocator = 0) const { - return Append(name, StrLen(name), allocator); + return Append(name, internal::StrLen(name), allocator); } #if RAPIDJSON_HAS_STDSTRING @@ -253,17 +263,18 @@ public: */ GenericPointer Append(SizeType index, Allocator* allocator = 0) const { char buffer[21]; - SizeType length = (sizeof(SizeType) == 4 ? internal::u32toa(index, buffer): internal::u64toa(index, buffer)) - buffer; + char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer); + SizeType length = static_cast(end - buffer); buffer[length] = '\0'; if (sizeof(Ch) == 1) { - Token token = { (Ch*)buffer, length, index }; + Token token = { reinterpret_cast(buffer), length, index }; return Append(token, allocator); } else { Ch name[21]; for (size_t i = 0; i <= length; i++) - name[i] = buffer[i]; + name[i] = static_cast(buffer[i]); Token token = { name, length, index }; return Append(token, allocator); } @@ -271,7 +282,7 @@ public: //! Append a token by value, and return a new Pointer /*! - \param value Value (either Uint or String) to be appended. + \param token token to be appended. \param allocator Allocator for the newly return Pointer. \return A new Pointer with appended token. */ @@ -299,6 +310,9 @@ public: //@} + //! Get the allocator of this pointer. + Allocator& GetAllocator() { return *allocator_; } + //!@name Tokens //@{ @@ -390,7 +404,7 @@ public: bool exist = true; for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { if (v->IsArray() && t->name[0] == '-' && t->length == 1) { - v->PushBack(Value().Move(), allocator); + v->PushBack(ValueType().Move(), allocator); v = &((*v)[v->Size() - 1]); exist = false; } @@ -408,7 +422,7 @@ public: if (t->index >= v->Size()) { v->Reserve(t->index + 1, allocator); while (t->index >= v->Size()) - v->PushBack(Value().Move(), allocator); + v->PushBack(ValueType().Move(), allocator); exist = false; } v = &((*v)[t->index]); @@ -416,7 +430,7 @@ public: else { typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); if (m == v->MemberEnd()) { - v->AddMember(Value(t->name, t->length, allocator).Move(), Value().Move(), allocator); + v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); v = &(--v->MemberEnd())->value; // Assumes AddMember() appends at the end exist = false; } @@ -435,7 +449,6 @@ public: //! Creates a value in a document. /*! \param document A document to be resolved. - \param allocator Allocator for creating the values if the specified value or its parents are not exist. \param alreadyExist If non-null, it stores whether the resolved value is already exist. \return The resolved newly created, or already exists value. */ @@ -452,9 +465,18 @@ public: //! Query a value in a subtree. /*! \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. \return Pointer to the value if it can be resolved. Otherwise null. + + \note + There are only 3 situations when a value cannot be resolved: + 1. A value in the path is not an array nor object. + 2. An object value does not contain the token. + 3. A token is out of range of an array value. + + Use unresolvedTokenIndex to retrieve the token index. */ - ValueType* Get(ValueType& root) const { + ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const { RAPIDJSON_ASSERT(IsValid()); ValueType* v = &root; for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { @@ -463,18 +485,23 @@ public: { typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); if (m == v->MemberEnd()) - return 0; + break; v = &m->value; } - break; + continue; case kArrayType: if (t->index == kPointerInvalidIndex || t->index >= v->Size()) - return 0; + break; v = &((*v)[t->index]); - break; + continue; default: - return 0; + break; } + + // Error: unresolved token + if (unresolvedTokenIndex) + *unresolvedTokenIndex = static_cast(t - tokens_); + return 0; } return v; } @@ -484,7 +511,9 @@ public: \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. \return Pointer to the value if it can be resolved. Otherwise null. */ - const ValueType* Get(const ValueType& root) const { return Get(const_cast(root)); } + const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const { + return Get(const_cast(root), unresolvedTokenIndex); + } //@} @@ -525,7 +554,7 @@ public: //! Query a value in a subtree with default primitive value. /*! - \tparam T \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) @@ -555,7 +584,7 @@ public: //! Query a value in a document with default primitive value. /*! - \tparam T \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) @@ -601,7 +630,7 @@ public: //! Set a primitive value in a subtree. /*! - \tparam T \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) @@ -637,7 +666,7 @@ public: //! Set a primitive value in a document. /*! - \tparam T \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) @@ -729,7 +758,7 @@ private: */ Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { if (!allocator_) // allocator is independently owned. - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) @@ -738,8 +767,12 @@ private: tokenCount_ = rhs.tokenCount_ + extraToken; tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch))); nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); - std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); - std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); + if (rhs.tokenCount_ > 0) { + std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); + } + if (nameBufferSize > 0) { + std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); + } // Adjust pointers to name buffer std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_; @@ -759,11 +792,13 @@ private: } //! Parse a JSON String or its URI fragment representation into tokens. +#ifndef __clang__ // -Wdocumentation /*! \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated. \param length Length of the source string. \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped. */ +#endif void Parse(const Ch* source, size_t length) { RAPIDJSON_ASSERT(source != NULL); RAPIDJSON_ASSERT(nameBuffer_ == 0); @@ -771,7 +806,7 @@ private: // Create own allocator if user did not supply. if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); // Count number of '/' as tokenCount tokenCount_ = 0; @@ -857,7 +892,7 @@ private: *name++ = c; } - token->length = name - token->name; + token->length = static_cast(name - token->name); if (token->length == 0) isNumber = false; *name++ = '\0'; // Null terminator @@ -944,6 +979,8 @@ private: */ class PercentDecodeStream { public: + typedef typename ValueType::Ch Ch; + //! Constructor /*! \param source Start of the stream @@ -959,11 +996,11 @@ private: src_++; Ch c = 0; for (int j = 0; j < 2; j++) { - c <<= 4; + c = static_cast(c << 4); Ch h = *src_; - if (h >= '0' && h <= '9') c += h - '0'; - else if (h >= 'A' && h <= 'F') c += h - 'A' + 10; - else if (h >= 'a' && h <= 'f') c += h - 'a' + 10; + if (h >= '0' && h <= '9') c = static_cast(c + h - '0'); + else if (h >= 'A' && h <= 'F') c = static_cast(c + h - 'A' + 10); + else if (h >= 'a' && h <= 'f') c = static_cast(c + h - 'a' + 10); else { valid_ = false; return 0; @@ -973,7 +1010,7 @@ private: return c; } - size_t Tell() const { return src_ - head_; } + size_t Tell() const { return static_cast(src_ - head_); } bool IsValid() const { return valid_; } private: @@ -992,8 +1029,8 @@ private: unsigned char u = static_cast(c); static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; os_.Put('%'); - os_.Put(hexDigits[u >> 4]); - os_.Put(hexDigits[u & 15]); + os_.Put(static_cast(hexDigits[u >> 4])); + os_.Put(static_cast(hexDigits[u & 15])); } private: OutputStream& os_; @@ -1041,23 +1078,23 @@ typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, c ////////////////////////////////////////////////////////////////////////////// template -typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer) { - return pointer.Get(root); +typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); } template -const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer) { - return pointer.Get(root); +const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); } template -typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N]) { - return GenericPointer(source, N - 1).Get(root); +typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); } template -const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N]) { - return GenericPointer(source, N - 1).Get(root); +const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); } ////////////////////////////////////////////////////////////////////////////// @@ -1310,4 +1347,12 @@ bool EraseValueByPointer(T& root, const CharType(&source)[N]) { RAPIDJSON_NAMESPACE_END +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + #endif // RAPIDJSON_POINTER_H_ diff --git a/contrib/rapidjson/include/rapidjson/prettywriter.h b/contrib/rapidjson/include/rapidjson/prettywriter.h index 416dd492e..98dfb3060 100644 --- a/contrib/rapidjson/include/rapidjson/prettywriter.h +++ b/contrib/rapidjson/include/rapidjson/prettywriter.h @@ -22,8 +22,21 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + RAPIDJSON_NAMESPACE_BEGIN +//! Combination of PrettyWriter format flags. +/*! \see PrettyWriter::SetFormatOptions + */ +enum PrettyFormatOptions { + kFormatDefault = 0, //!< Default pretty formatting. + kFormatSingleLineArray = 1 //!< Format arrays on a single line. +}; + //! Writer with indentation and spacing. /*! \tparam OutputStream Type of ouptut os. @@ -31,10 +44,10 @@ RAPIDJSON_NAMESPACE_BEGIN \tparam TargetEncoding Encoding of output stream. \tparam StackAllocator Type of allocator for allocating memory of stack. */ -template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator> -class PrettyWriter : public Writer { +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class PrettyWriter : public Writer { public: - typedef Writer Base; + typedef Writer Base; typedef typename Base::Ch Ch; //! Constructor @@ -42,8 +55,17 @@ public: \param allocator User supplied allocator. If it is null, it will create a private one. \param levelDepth Initial capacity of stack. */ - PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : - Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} + explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} + + + explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + PrettyWriter(PrettyWriter&& rhs) : + Base(std::forward(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {} +#endif //! Set custom indentation. /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). @@ -57,6 +79,14 @@ public: return *this; } + //! Set pretty writer formatting options. + /*! \param options Formatting options. + */ + PrettyWriter& SetFormatOptions(PrettyFormatOptions options) { + formatOptions_ = options; + return *this; + } + /*! @name Implementation of Handler \see Handler */ @@ -70,7 +100,15 @@ public: bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + PrettyPrefix(kNumberType); + return Base::WriteString(str, length); + } + bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); (void)copy; PrettyPrefix(kStringType); return Base::WriteString(str, length); @@ -89,11 +127,19 @@ public: } bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) { + return Key(str.data(), SizeType(str.size())); + } +#endif bool EndObject(SizeType memberCount = 0) { (void)memberCount; - RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); - RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; if (!empty) { @@ -104,7 +150,7 @@ public: (void)ret; RAPIDJSON_ASSERT(ret == true); if (Base::level_stack_.Empty()) // end of json text - Base::os_->Flush(); + Base::Flush(); return true; } @@ -120,7 +166,7 @@ public: RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; - if (!empty) { + if (!empty && !(formatOptions_ & kFormatSingleLineArray)) { Base::os_->Put('\n'); WriteIndent(); } @@ -128,7 +174,7 @@ public: (void)ret; RAPIDJSON_ASSERT(ret == true); if (Base::level_stack_.Empty()) // end of json text - Base::os_->Flush(); + Base::Flush(); return true; } @@ -142,6 +188,22 @@ public: bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. + */ + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + PrettyPrefix(type); + return Base::WriteRawValue(json, length); + } + protected: void PrettyPrefix(Type type) { (void)type; @@ -151,11 +213,14 @@ protected: if (level->inArray) { if (level->valueCount > 0) { Base::os_->Put(','); // add comma if it is not the first element in array - Base::os_->Put('\n'); + if (formatOptions_ & kFormatSingleLineArray) + Base::os_->Put(' '); } - else + + if (!(formatOptions_ & kFormatSingleLineArray)) { Base::os_->Put('\n'); - WriteIndent(); + WriteIndent(); + } } else { // in object if (level->valueCount > 0) { @@ -186,11 +251,12 @@ protected: void WriteIndent() { size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; - PutN(*Base::os_, indentChar_, count); + PutN(*Base::os_, static_cast(indentChar_), count); } Ch indentChar_; unsigned indentCharCount_; + PrettyFormatOptions formatOptions_; private: // Prohibit copy constructor & assignment operator. @@ -200,6 +266,10 @@ private: RAPIDJSON_NAMESPACE_END +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif diff --git a/contrib/rapidjson/include/rapidjson/rapidjson.h b/contrib/rapidjson/include/rapidjson/rapidjson.h index f22130d3c..5716fdc06 100644 --- a/contrib/rapidjson/include/rapidjson/rapidjson.h +++ b/contrib/rapidjson/include/rapidjson/rapidjson.h @@ -49,6 +49,11 @@ // token stringification #define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) #define RAPIDJSON_DO_STRINGIFY(x) #x + +// token concatenation +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y //!@endcond /*! \def RAPIDJSON_MAJOR_VERSION @@ -68,8 +73,8 @@ \brief Version of RapidJSON in ".." string format. */ #define RAPIDJSON_MAJOR_VERSION 1 -#define RAPIDJSON_MINOR_VERSION 0 -#define RAPIDJSON_PATCH_VERSION 2 +#define RAPIDJSON_MINOR_VERSION 1 +#define RAPIDJSON_PATCH_VERSION 0 #define RAPIDJSON_VERSION_STRING \ RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION) @@ -119,6 +124,31 @@ #define RAPIDJSON_NAMESPACE_END } #endif +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_HAS_STDSTRING + +#ifndef RAPIDJSON_HAS_STDSTRING +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation +#else +#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default +#endif +/*! \def RAPIDJSON_HAS_STDSTRING + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for \c std::string + + By defining this preprocessor symbol to \c 1, several convenience functions for using + \ref rapidjson::GenericValue with \c std::string are enabled, especially + for construction and comparison. + + \hideinitializer +*/ +#endif // !defined(RAPIDJSON_HAS_STDSTRING) + +#if RAPIDJSON_HAS_STDSTRING +#include +#endif // RAPIDJSON_HAS_STDSTRING + /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_NO_INT64DEFINE @@ -134,7 +164,7 @@ */ #ifndef RAPIDJSON_NO_INT64DEFINE //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#ifdef _MSC_VER +#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013 #include "msinttypes/stdint.h" #include "msinttypes/inttypes.h" #else @@ -153,9 +183,9 @@ #ifndef RAPIDJSON_FORCEINLINE //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#if defined(_MSC_VER) && !defined(NDEBUG) +#if defined(_MSC_VER) && defined(NDEBUG) #define RAPIDJSON_FORCEINLINE __forceinline -#elif defined(__GNUC__) && __GNUC__ >= 4 && !defined(NDEBUG) +#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG) #define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) #else #define RAPIDJSON_FORCEINLINE @@ -211,6 +241,8 @@ # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64)) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN # elif defined(RAPIDJSON_DOXYGEN_RUNNING) # define RAPIDJSON_ENDIAN # else @@ -223,7 +255,7 @@ //! Whether using 64-bit architecture #ifndef RAPIDJSON_64BIT -#if defined(__LP64__) || defined(_WIN64) +#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__) #define RAPIDJSON_64BIT 1 #else #define RAPIDJSON_64BIT 0 @@ -238,13 +270,14 @@ \param x pointer to align Some machines require strict data alignment. Currently the default uses 4 bytes - alignment. User can customize by defining the RAPIDJSON_ALIGN function macro., + alignment on 32-bit platforms and 8 bytes alignment for 64-bit platforms. + User can customize by defining the RAPIDJSON_ALIGN function macro. */ #ifndef RAPIDJSON_ALIGN #if RAPIDJSON_64BIT == 1 -#define RAPIDJSON_ALIGN(x) ((x + 7u) & ~7u) +#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) #else -#define RAPIDJSON_ALIGN(x) ((x + 3u) & ~3u) +#define RAPIDJSON_ALIGN(x) (((x) + 3u) & ~3u) #endif #endif @@ -262,17 +295,47 @@ #endif /////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD +// RAPIDJSON_48BITPOINTER_OPTIMIZATION + +//! Use only lower 48-bit address for some pointers. +/*! + \ingroup RAPIDJSON_CONFIG + + This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address. + The higher 16-bit can be used for storing other data. + \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture. +*/ +#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1 +#else +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 +#endif +#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION + +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1 +#if RAPIDJSON_64BIT != 1 +#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1 +#endif +#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast((reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast(reinterpret_cast(x)))) +#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast(reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF)))) +#else +#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x)) +#define RAPIDJSON_GETPOINTER(type, p) (p) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD /*! \def RAPIDJSON_SIMD \ingroup RAPIDJSON_CONFIG - \brief Enable SSE2/SSE4.2 optimization. + \brief Enable SSE2/SSE4.2/Neon optimization. RapidJSON supports optimized implementations for some parsing operations - based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible - processors. + based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel + or ARM compatible processors. - To enable these optimizations, two different symbols can be defined; + To enable these optimizations, three different symbols can be defined; \code // Enable SSE2 optimization. #define RAPIDJSON_SSE2 @@ -281,13 +344,17 @@ #define RAPIDJSON_SSE42 \endcode - \c RAPIDJSON_SSE42 takes precedence, if both are defined. + // Enable ARM Neon optimization. + #define RAPIDJSON_NEON + \endcode + + \c RAPIDJSON_SSE42 takes precedence over SSE2, if both are defined. If any of these symbols is defined, RapidJSON defines the macro \c RAPIDJSON_SIMD to indicate the availability of the optimized code. */ #if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ - || defined(RAPIDJSON_DOXYGEN_RUNNING) + || defined(RAPIDJSON_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING) #define RAPIDJSON_SIMD #endif @@ -347,25 +414,33 @@ RAPIDJSON_NAMESPACE_END /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_STATIC_ASSERT -// Adopt from boost +// Prefer C++11 static_assert, if available #ifndef RAPIDJSON_STATIC_ASSERT +#if __cplusplus >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 ) +#define RAPIDJSON_STATIC_ASSERT(x) \ + static_assert(x, RAPIDJSON_STRINGIFY(x)) +#endif // C++11 +#endif // RAPIDJSON_STATIC_ASSERT + +// Adopt C++03 implementation from boost +#ifndef RAPIDJSON_STATIC_ASSERT +#ifndef __clang__ //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#endif RAPIDJSON_NAMESPACE_BEGIN template struct STATIC_ASSERTION_FAILURE; template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; -template struct StaticAssertTest {}; +template struct StaticAssertTest {}; RAPIDJSON_NAMESPACE_END -#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) -#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) -#define RAPIDJSON_DO_JOIN2(X, Y) X##Y - #if defined(__GNUC__) #define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) #else #define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE #endif +#ifndef __clang__ //!@endcond +#endif /*! \def RAPIDJSON_STATIC_ASSERT \brief (Internal) macro to check for conditions at compile-time @@ -376,6 +451,35 @@ RAPIDJSON_NAMESPACE_END typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif // RAPIDJSON_STATIC_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY + +//! Compiler branching hint for expression with high probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression likely to be true. +*/ +#ifndef RAPIDJSON_LIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define RAPIDJSON_LIKELY(x) (x) +#endif +#endif + +//! Compiler branching hint for expression with low probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression unlikely to be true. +*/ +#ifndef RAPIDJSON_UNLIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define RAPIDJSON_UNLIKELY(x) (x) +#endif #endif /////////////////////////////////////////////////////////////////////////////// @@ -438,8 +542,12 @@ RAPIDJSON_NAMESPACE_END #ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS #if defined(__clang__) -#define RAPIDJSON_HAS_CXX11_RVALUE_REFS __has_feature(cxx_rvalue_references) && \ +#if __has_feature(cxx_rvalue_references) && \ (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif #elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ (defined(_MSC_VER) && _MSC_VER >= 1600) @@ -470,6 +578,17 @@ RAPIDJSON_NAMESPACE_END #define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 #endif +#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 +#else +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR + //!@endcond /////////////////////////////////////////////////////////////////////////////// @@ -477,7 +596,7 @@ RAPIDJSON_NAMESPACE_END #ifndef RAPIDJSON_NEW ///! customization point for global \c new -#define RAPIDJSON_NEW(x) new x +#define RAPIDJSON_NEW(TypeName) new TypeName #endif #ifndef RAPIDJSON_DELETE ///! customization point for global \c delete @@ -485,10 +604,7 @@ RAPIDJSON_NAMESPACE_END #endif /////////////////////////////////////////////////////////////////////////////// -// Allocators and Encodings - -#include "allocators.h" -#include "encodings.h" +// Type /*! \namespace rapidjson \brief main RapidJSON namespace @@ -496,148 +612,6 @@ RAPIDJSON_NAMESPACE_END */ RAPIDJSON_NAMESPACE_BEGIN -/////////////////////////////////////////////////////////////////////////////// -// Stream - -/*! \class rapidjson::Stream - \brief Concept for reading and writing characters. - - For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). - - For write-only stream, only need to implement Put() and Flush(). - -\code -concept Stream { - typename Ch; //!< Character type of the stream. - - //! Read the current character from stream without moving the read cursor. - Ch Peek() const; - - //! Read the current character from stream and moving the read cursor to next character. - Ch Take(); - - //! Get the current read cursor. - //! \return Number of characters read from start. - size_t Tell(); - - //! Begin writing operation at the current read pointer. - //! \return The begin writer pointer. - Ch* PutBegin(); - - //! Write a character. - void Put(Ch c); - - //! Flush the buffer. - void Flush(); - - //! End the writing operation. - //! \param begin The begin write pointer returned by PutBegin(). - //! \return Number of characters written. - size_t PutEnd(Ch* begin); -} -\endcode -*/ - -//! Provides additional information for stream. -/*! - By using traits pattern, this type provides a default configuration for stream. - For custom stream, this type can be specialized for other configuration. - See TEST(Reader, CustomStringStream) in readertest.cpp for example. -*/ -template -struct StreamTraits { - //! Whether to make local copy of stream for optimization during parsing. - /*! - By default, for safety, streams do not use local copy optimization. - Stream that can be copied fast should specialize this, like StreamTraits. - */ - enum { copyOptimization = 0 }; -}; - -//! Put N copies of a character to a stream. -template -inline void PutN(Stream& stream, Ch c, size_t n) { - for (size_t i = 0; i < n; i++) - stream.Put(c); -} - -/////////////////////////////////////////////////////////////////////////////// -// StringStream - -//! Read-only string stream. -/*! \note implements Stream concept -*/ -template -struct GenericStringStream { - typedef typename Encoding::Ch Ch; - - GenericStringStream(const Ch *src) : src_(src), head_(src) {} - - Ch Peek() const { return *src_; } - Ch Take() { return *src_++; } - size_t Tell() const { return static_cast(src_ - head_); } - - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - void Put(Ch) { RAPIDJSON_ASSERT(false); } - void Flush() { RAPIDJSON_ASSERT(false); } - size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } - - const Ch* src_; //!< Current read position. - const Ch* head_; //!< Original head of the string. -}; - -template -struct StreamTraits > { - enum { copyOptimization = 1 }; -}; - -//! String stream with UTF8 encoding. -typedef GenericStringStream > StringStream; - -/////////////////////////////////////////////////////////////////////////////// -// InsituStringStream - -//! A read-write string stream. -/*! This string stream is particularly designed for in-situ parsing. - \note implements Stream concept -*/ -template -struct GenericInsituStringStream { - typedef typename Encoding::Ch Ch; - - GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} - - // Read - Ch Peek() { return *src_; } - Ch Take() { return *src_++; } - size_t Tell() { return static_cast(src_ - head_); } - - // Write - void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } - - Ch* PutBegin() { return dst_ = src_; } - size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } - void Flush() {} - - Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } - void Pop(size_t count) { dst_ -= count; } - - Ch* src_; - Ch* dst_; - Ch* head_; -}; - -template -struct StreamTraits > { - enum { copyOptimization = 1 }; -}; - -//! Insitu string stream with UTF8 encoding. -typedef GenericInsituStringStream > InsituStringStream; - -/////////////////////////////////////////////////////////////////////////////// -// Type - //! Type of JSON value enum Type { kNullType = 0, //!< null diff --git a/contrib/rapidjson/include/rapidjson/reader.h b/contrib/rapidjson/include/rapidjson/reader.h index c5ecf4be5..120c31115 100644 --- a/contrib/rapidjson/include/rapidjson/reader.h +++ b/contrib/rapidjson/include/rapidjson/reader.h @@ -1,5 +1,5 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// +// // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except @@ -7,9 +7,9 @@ // // http://opensource.org/licenses/MIT // -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_READER_H_ @@ -17,11 +17,13 @@ /*! \file reader.h */ -#include "rapidjson.h" -#include "encodings.h" +#include "allocators.h" +#include "stream.h" +#include "encodedstream.h" #include "internal/meta.h" #include "internal/stack.h" #include "internal/strtod.h" +#include #if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) #include @@ -31,6 +33,8 @@ #include #elif defined(RAPIDJSON_SSE2) #include +#elif defined(RAPIDJSON_NEON) +#include #endif #ifdef _MSC_VER @@ -39,6 +43,13 @@ RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant RAPIDJSON_DIAG_OFF(4702) // unreachable code #endif +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(old-style-cast) +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +#endif + #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) @@ -49,7 +60,7 @@ RAPIDJSON_DIAG_OFF(effc++) #ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN #define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ RAPIDJSON_MULTILINEMACRO_BEGIN \ - if (HasParseError()) { return value; } \ + if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \ RAPIDJSON_MULTILINEMACRO_END #endif #define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ @@ -120,7 +131,7 @@ RAPIDJSON_NAMESPACE_BEGIN /////////////////////////////////////////////////////////////////////////////// // ParseFlag -/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS +/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS \ingroup RAPIDJSON_CONFIG \brief User-defined kParseDefaultFlags definition. @@ -140,6 +151,10 @@ enum ParseFlag { kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). + kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments. + kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings. + kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays. + kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles. kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS }; @@ -148,7 +163,7 @@ enum ParseFlag { /*! \class rapidjson::Handler \brief Concept for receiving events from GenericReader upon parsing. - The functions return true if no error occurs. If they return false, + The functions return true if no error occurs. If they return false, the event publisher should terminate the process. \code concept Handler { @@ -161,6 +176,8 @@ concept Handler { bool Int64(int64_t i); bool Uint64(uint64_t i); bool Double(double d); + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType length, bool copy); bool String(const Ch* str, SizeType length, bool copy); bool StartObject(); bool Key(const Ch* str, SizeType length, bool copy); @@ -191,6 +208,8 @@ struct BaseReaderHandler { bool Int64(int64_t) { return static_cast(*this).Default(); } bool Uint64(uint64_t) { return static_cast(*this).Default(); } bool Double(double) { return static_cast(*this).Default(); } + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } bool StartObject() { return static_cast(*this).Default(); } bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } @@ -248,10 +267,17 @@ void SkipWhitespace(InputStream& is) { internal::StreamLocalCopy copy(is); InputStream& s(copy.s); - while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t') + typename InputStream::Ch c; + while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t') s.Take(); } +inline const char* SkipWhitespace(const char* p, const char* end) { + while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + return p; +} + #ifdef RAPIDJSON_SSE42 //! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. inline const char *SkipWhitespace_SIMD(const char* p) { @@ -262,7 +288,7 @@ inline const char *SkipWhitespace_SIMD(const char* p) { return p; // 16-byte align to the next boundary - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & ~15); + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); while (p != nextAligned) if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') ++p; @@ -271,23 +297,37 @@ inline const char *SkipWhitespace_SIMD(const char* p) { // The rest of string using SIMD static const char whitespace[16] = " \n\r\t"; - const __m128i w = _mm_load_si128((const __m128i *)&whitespace[0]); + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); for (;; p += 16) { - const __m128i s = _mm_load_si128((const __m128i *)p); - const unsigned r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); - if (r != 0) { // some of characters is non-whitespace -#ifdef _MSC_VER // Find the index of first non-whitespace - unsigned long offset; - _BitScanForward(&offset, r); - return p + offset; -#else - return p + __builtin_ffs(r) - 1; -#endif - } + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; } } +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The middle of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; + } + + return SkipWhitespace(p, end); +} + #elif defined(RAPIDJSON_SSE2) //! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. @@ -299,7 +339,7 @@ inline const char *SkipWhitespace_SIMD(const char* p) { return p; // 16-byte align to the next boundary - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & ~15); + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); while (p != nextAligned) if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') ++p; @@ -307,24 +347,22 @@ inline const char *SkipWhitespace_SIMD(const char* p) { return p; // The rest of string - static const char whitespaces[4][17] = { - " ", - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", - "\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"}; + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 - const __m128i w0 = _mm_loadu_si128((const __m128i *)&whitespaces[0][0]); - const __m128i w1 = _mm_loadu_si128((const __m128i *)&whitespaces[1][0]); - const __m128i w2 = _mm_loadu_si128((const __m128i *)&whitespaces[2][0]); - const __m128i w3 = _mm_loadu_si128((const __m128i *)&whitespaces[3][0]); + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); for (;; p += 16) { - const __m128i s = _mm_load_si128((const __m128i *)p); + const __m128i s = _mm_load_si128(reinterpret_cast(p)); __m128i x = _mm_cmpeq_epi8(s, w0); x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); - unsigned short r = (unsigned short)~_mm_movemask_epi8(x); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); if (r != 0) { // some of characters may be non-whitespace #ifdef _MSC_VER // Find the index of first non-whitespace unsigned long offset; @@ -337,11 +375,134 @@ inline const char *SkipWhitespace_SIMD(const char* p) { } } -#endif // RAPIDJSON_SSE2 +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } + + return SkipWhitespace(p, end); +} + +#elif defined(RAPIDJSON_NEON) + +//! Skip whitespace with ARM Neon instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + if (low == 0) { + if (high != 0) { + int lz =__builtin_clzll(high);; + return p + 8 + (lz >> 3); + } + } else { + int lz = __builtin_clzll(low);; + return p + (lz >> 3); + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (; p <= end - 16; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + if (low == 0) { + if (high != 0) { + int lz = __builtin_clzll(high); + return p + 8 + (lz >> 3); + } + } else { + int lz = __builtin_clzll(low); + return p + (lz >> 3); + } + } + + return SkipWhitespace(p, end); +} + +#endif // RAPIDJSON_NEON #ifdef RAPIDJSON_SIMD //! Template function specialization for InsituStringStream -template<> inline void SkipWhitespace(InsituStringStream& is) { +template<> inline void SkipWhitespace(InsituStringStream& is) { is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); } @@ -349,23 +510,27 @@ template<> inline void SkipWhitespace(InsituStringStream& is) { template<> inline void SkipWhitespace(StringStream& is) { is.src_ = SkipWhitespace_SIMD(is.src_); } + +template<> inline void SkipWhitespace(EncodedInputStream, MemoryStream>& is) { + is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_); +} #endif // RAPIDJSON_SIMD /////////////////////////////////////////////////////////////////////////////// // GenericReader //! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. -/*! GenericReader parses JSON text from a stream, and send events synchronously to an +/*! GenericReader parses JSON text from a stream, and send events synchronously to an object implementing Handler concept. - It needs to allocate a stack for storing a single decoded string during + It needs to allocate a stack for storing a single decoded string during non-destructive parsing. - For in-situ parsing, the decoded string is directly written to the source + For in-situ parsing, the decoded string is directly written to the source text string, no temporary buffer is required. A GenericReader object can be reused for parsing multiple JSON text. - + \tparam SourceEncoding Encoding of the input stream. \tparam TargetEncoding Encoding of the parse output. \tparam StackAllocator Allocator type for stack. @@ -398,9 +563,10 @@ public: ClearStackOnExit scope(*this); - SkipWhitespace(is); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); - if (is.Peek() == '\0') { + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); } @@ -409,9 +575,10 @@ public: RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); if (!(parseFlags & kParseStopWhenDoneFlag)) { - SkipWhitespace(is); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); - if (is.Peek() != '\0') { + if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); } @@ -433,9 +600,86 @@ public: return Parse(is, handler); } + //! Initialize JSON text token-by-token parsing + /*! + */ + void IterativeParseInit() { + parseResult_.Clear(); + state_ = IterativeParsingStartState; + } + + //! Parse one token from JSON text + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + bool IterativeParseNext(InputStream& is, Handler& handler) { + while (RAPIDJSON_LIKELY(is.Peek() != '\0')) { + SkipWhitespaceAndComments(is); + + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state_, t); + IterativeParsingState d = Transit(state_, t, n, is, handler); + + // If we've finished or hit an error... + if (RAPIDJSON_UNLIKELY(IsIterativeParsingCompleteState(d))) { + // Report errors. + if (d == IterativeParsingErrorState) { + HandleError(state_, is); + return false; + } + + // Transition to the finish state. + RAPIDJSON_ASSERT(d == IterativeParsingFinishState); + state_ = d; + + // If StopWhenDone is not set... + if (!(parseFlags & kParseStopWhenDoneFlag)) { + // ... and extra non-whitespace data is found... + SkipWhitespaceAndComments(is); + if (is.Peek() != '\0') { + // ... this is considered an error. + HandleError(state_, is); + return false; + } + } + + // Success! We are done! + return true; + } + + // Transition to the new state. + state_ = d; + + // If we parsed anything other than a delimiter, we invoked the handler, so we can return true now. + if (!IsIterativeParsingDelimiterState(n)) + return true; + } + + // We reached the end of file. + stack_.Clear(); + + if (state_ != IterativeParsingFinishState) { + HandleError(state_, is); + return false; + } + + return true; + } + + //! Check if token-by-token parsing JSON text is complete + /*! \return Whether the JSON has been fully decoded. + */ + RAPIDJSON_FORCEINLINE bool IterativeParseComplete() { + return IsIterativeParsingCompleteState(state_); + } + //! Whether a parse error has occured in the last parsing. bool HasParseError() const { return parseResult_.IsError(); } - + //! Get the \ref ParseErrorCode of last parsing. ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } @@ -462,52 +706,98 @@ private: ClearStackOnExit& operator=(const ClearStackOnExit&); }; + template + void SkipWhitespaceAndComments(InputStream& is) { + SkipWhitespace(is); + + if (parseFlags & kParseCommentsFlag) { + while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) { + if (Consume(is, '*')) { + while (true) { + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + else if (Consume(is, '*')) { + if (Consume(is, '/')) + break; + } + else + is.Take(); + } + } + else if (RAPIDJSON_LIKELY(Consume(is, '/'))) + while (is.Peek() != '\0' && is.Take() != '\n') {} + else + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + + SkipWhitespace(is); + } + } + } + // Parse object: { string : value, ... } template void ParseObject(InputStream& is, Handler& handler) { RAPIDJSON_ASSERT(is.Peek() == '{'); is.Take(); // Skip '{' - - if (!handler.StartObject()) + + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - SkipWhitespace(is); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - if (is.Peek() == '}') { - is.Take(); - if (!handler.EndObject(0)) // empty object + if (Consume(is, '}')) { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); return; } for (SizeType memberCount = 0;;) { - if (is.Peek() != '"') + if (RAPIDJSON_UNLIKELY(is.Peek() != '"')) RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); ParseString(is, handler, true); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - SkipWhitespace(is); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - if (is.Take() != ':') + if (RAPIDJSON_UNLIKELY(!Consume(is, ':'))) RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); - SkipWhitespace(is); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; ParseValue(is, handler); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - SkipWhitespace(is); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; ++memberCount; - switch (is.Take()) { - case ',': SkipWhitespace(is); break; - case '}': - if (!handler.EndObject(memberCount)) + switch (is.Peek()) { + case ',': + is.Take(); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + break; + case '}': + is.Take(); + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); return; - default: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); + default: + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy + } + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == '}') { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } } } } @@ -517,15 +807,15 @@ private: void ParseArray(InputStream& is, Handler& handler) { RAPIDJSON_ASSERT(is.Peek() == '['); is.Take(); // Skip '[' - - if (!handler.StartArray()) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - - SkipWhitespace(is); - if (is.Peek() == ']') { - is.Take(); - if (!handler.EndArray(0)) // empty array + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); return; } @@ -535,15 +825,28 @@ private: RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; ++elementCount; - SkipWhitespace(is); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - switch (is.Take()) { - case ',': SkipWhitespace(is); break; - case ']': - if (!handler.EndArray(elementCount)) + if (Consume(is, ',')) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + } + else if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == ']') { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); return; - default: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + } } } } @@ -553,12 +856,12 @@ private: RAPIDJSON_ASSERT(is.Peek() == 'n'); is.Take(); - if (is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l') { - if (!handler.Null()) + if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) { + if (RAPIDJSON_UNLIKELY(!handler.Null())) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); } else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); } template @@ -566,12 +869,12 @@ private: RAPIDJSON_ASSERT(is.Peek() == 't'); is.Take(); - if (is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e') { - if (!handler.Bool(true)) + if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(true))) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); } else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); } template @@ -579,20 +882,30 @@ private: RAPIDJSON_ASSERT(is.Peek() == 'f'); is.Take(); - if (is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e') { - if (!handler.Bool(false)) + if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(false))) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); } else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) { + if (RAPIDJSON_LIKELY(is.Peek() == expect)) { + is.Take(); + return true; + } + else + return false; } // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). template - unsigned ParseHex4(InputStream& is) { + unsigned ParseHex4(InputStream& is, size_t escapeOffset) { unsigned codepoint = 0; for (int i = 0; i < 4; i++) { - Ch c = is.Take(); + Ch c = is.Peek(); codepoint <<= 4; codepoint += static_cast(c); if (c >= '0' && c <= '9') @@ -602,9 +915,10 @@ private: else if (c >= 'a' && c <= 'f') codepoint -= 'a' - 10; else { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); } + is.Take(); } return codepoint; } @@ -619,7 +933,14 @@ private: *stack_.template Push() = c; ++length_; } + + RAPIDJSON_FORCEINLINE void* Push(SizeType count) { + length_ += count; + return stack_.template Push(count); + } + size_t Length() const { return length_; } + Ch* Pop() { return stack_.template Pop(length_); } @@ -638,6 +959,9 @@ private: internal::StreamLocalCopy copy(is); InputStream& s(copy.s); + RAPIDJSON_ASSERT(s.Peek() == '\"'); + s.Take(); // Skip '\"' + bool success = false; if (parseFlags & kParseInsituFlag) { typename InputStream::Ch *head = s.PutBegin(); @@ -645,7 +969,7 @@ private: RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; size_t length = s.PutEnd(head) - 1; RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); - const typename TargetEncoding::Ch* const str = (typename TargetEncoding::Ch*)head; + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); } else { @@ -656,7 +980,7 @@ private: const typename TargetEncoding::Ch* const str = stackStream.Pop(); success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); } - if (!success) + if (RAPIDJSON_UNLIKELY(!success)) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); } @@ -667,74 +991,421 @@ private: //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN #define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 static const char escape[256] = { - Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', - Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, - 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, - 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', + Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, + 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, + 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 }; #undef Z16 //!@endcond - RAPIDJSON_ASSERT(is.Peek() == '\"'); - is.Take(); // Skip '\"' - for (;;) { + // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation. + if (!(parseFlags & kParseValidateEncodingFlag)) + ScanCopyUnescapedString(is, os); + Ch c = is.Peek(); - if (c == '\\') { // Escape + if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape + size_t escapeOffset = is.Tell(); // For invalid escaping, report the inital '\\' as error offset is.Take(); - Ch e = is.Take(); - if ((sizeof(Ch) == 1 || unsigned(e) < 256) && escape[(unsigned char)e]) { - os.Put(escape[(unsigned char)e]); + Ch e = is.Peek(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { + is.Take(); + os.Put(static_cast(escape[static_cast(e)])); } - else if (e == 'u') { // Unicode - unsigned codepoint = ParseHex4(is); + else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode + is.Take(); + unsigned codepoint = ParseHex4(is, escapeOffset); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - if (codepoint >= 0xD800 && codepoint <= 0xDBFF) { + if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDBFF)) { // Handle UTF-16 surrogate pair - if (is.Take() != '\\' || is.Take() != 'u') - RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); - unsigned codepoint2 = ParseHex4(is); + if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + unsigned codepoint2 = ParseHex4(is, escapeOffset); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - if (codepoint2 < 0xDC00 || codepoint2 > 0xDFFF) - RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); + if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; } TEncoding::Encode(os, codepoint); } else - RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); } - else if (c == '"') { // Closing double quote + else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote is.Take(); os.Put('\0'); // null-terminate the string return; } - else if (c == '\0') - RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell() - 1); - else if ((unsigned)c < 0x20) // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF - RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); - else { - if (parseFlags & kParseValidateEncodingFlag ? - !Transcoder::Validate(is, os) : - !Transcoder::Transcode(is, os)) + else if (RAPIDJSON_UNLIKELY(static_cast(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + if (c == '\0') + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); + else RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); } + else { + size_t offset = is.Tell(); + if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? + !Transcoder::Validate(is, os) : + !Transcoder::Transcode(is, os)))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); + } } } - template + template + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) { + // Do nothing for generic version + } + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType length; + #ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; + #else + length = static_cast(__builtin_ffs(r) - 1); + #endif + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16, q += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + for (const char* pend = p + length; p != pend; ) + *q++ = *p++; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + p += length; + break; + } + } + + is.src_ = is.dst_ = p; + } +#elif defined(RAPIDJSON_NEON) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + unsigned lz = (unsigned)__builtin_clzll(high);; + length = 8 + (lz >> 3); + escaped = true; + } + } else { + unsigned lz = (unsigned)__builtin_clzll(low);; + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + vst1q_u8(reinterpret_cast(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16, q += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + unsigned lz = (unsigned)__builtin_clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + unsigned lz = (unsigned)__builtin_clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + for (const char* pend = p + length; p != pend; ) { + *q++ = *p++; + } + break; + } + vst1q_u8(reinterpret_cast(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + if (low == 0) { + if (high != 0) { + int lz = __builtin_clzll(high); + p += 8 + (lz >> 3); + break; + } + } else { + int lz = __builtin_clzll(low); + p += lz >> 3; + break; + } + } + + is.src_ = is.dst_ = p; + } +#endif // RAPIDJSON_NEON + + template class NumberStream; template - class NumberStream { + class NumberStream { public: + typedef typename InputStream::Ch Ch; + NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } - ~NumberStream() {} RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } + RAPIDJSON_FORCEINLINE void Push(char) {} + size_t Tell() { return is.Tell(); } size_t Length() { return 0; } const char* Pop() { return 0; } @@ -746,17 +1417,20 @@ private: }; template - class NumberStream : public NumberStream { - typedef NumberStream Base; + class NumberStream : public NumberStream { + typedef NumberStream Base; public: - NumberStream(GenericReader& reader, InputStream& is) : NumberStream(reader, is), stackStream(reader.stack_) {} - ~NumberStream() {} + NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {} RAPIDJSON_FORCEINLINE Ch TakePush() { - stackStream.Put((char)Base::is.Peek()); + stackStream.Put(static_cast(Base::is.Peek())); return Base::is.Take(); } + RAPIDJSON_FORCEINLINE void Push(char c) { + stackStream.Put(c); + } + size_t Length() { return stackStream.Length(); } const char* Pop() { @@ -768,34 +1442,48 @@ private: StackStream stackStream; }; + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {} + + RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } + }; + template void ParseNumber(InputStream& is, Handler& handler) { internal::StreamLocalCopy copy(is); - NumberStream s(*this, copy.s); + NumberStream s(*this, copy.s); + + size_t startOffset = s.Tell(); + double d = 0.0; + bool useNanOrInf = false; // Parse minus - bool minus = false; - if (s.Peek() == '-') { - minus = true; - s.Take(); - } + bool minus = Consume(s, '-'); // Parse int: zero / ( digit1-9 *DIGIT ) unsigned i = 0; uint64_t i64 = 0; bool use64bit = false; int significandDigit = 0; - if (s.Peek() == '0') { + if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) { i = 0; s.TakePush(); } - else if (s.Peek() >= '1' && s.Peek() <= '9') { + else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) { i = static_cast(s.TakePush() - '0'); if (minus) - while (s.Peek() >= '0' && s.Peek() <= '9') { - if (i >= 214748364) { // 2^31 = 2147483648 - if (i != 214748364 || s.Peek() > '8') { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648 + if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) { i64 = i; use64bit = true; break; @@ -805,9 +1493,9 @@ private: significandDigit++; } else - while (s.Peek() >= '0' && s.Peek() <= '9') { - if (i >= 429496729) { // 2^32 - 1 = 4294967295 - if (i != 429496729 || s.Peek() > '5') { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295 + if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) { i64 = i; use64bit = true; break; @@ -817,18 +1505,41 @@ private: significandDigit++; } } + // Parse NaN or Infinity here + else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { + if (Consume(s, 'N')) { + if (Consume(s, 'a') && Consume(s, 'N')) { + d = std::numeric_limits::quiet_NaN(); + useNanOrInf = true; + } + } + else if (RAPIDJSON_LIKELY(Consume(s, 'I'))) { + if (Consume(s, 'n') && Consume(s, 'f')) { + d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); + useNanOrInf = true; + + if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') + && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } + } + + if (RAPIDJSON_UNLIKELY(!useNanOrInf)) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } else RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); // Parse 64bit int bool useDouble = false; - double d = 0.0; if (use64bit) { - if (minus) - while (s.Peek() >= '0' && s.Peek() <= '9') { - if (i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC)) // 2^63 = 9223372036854775808 - if (i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8') { - d = i64; + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) { + d = static_cast(i64); useDouble = true; break; } @@ -836,10 +1547,10 @@ private: significandDigit++; } else - while (s.Peek() >= '0' && s.Peek() <= '9') { - if (i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999)) // 2^64 - 1 = 18446744073709551615 - if (i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5') { - d = i64; + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) { + d = static_cast(i64); useDouble = true; break; } @@ -850,9 +1561,9 @@ private: // Force double for big integer if (useDouble) { - while (s.Peek() >= '0' && s.Peek() <= '9') { - if (d >= 1.7976931348623157e307) // DBL_MAX / 10.0 - RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(d >= 1.7976931348623157e307)) // DBL_MAX / 10.0 + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); d = d * 10 + (s.TakePush() - '0'); } } @@ -860,11 +1571,10 @@ private: // Parse frac = decimal-point 1*DIGIT int expFrac = 0; size_t decimalPosition; - if (s.Peek() == '.') { - s.Take(); + if (Consume(s, '.')) { decimalPosition = s.Length(); - if (!(s.Peek() >= '0' && s.Peek() <= '9')) + if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); if (!useDouble) { @@ -872,8 +1582,8 @@ private: // Use i64 to store significand in 64-bit architecture if (!use64bit) i64 = i; - - while (s.Peek() >= '0' && s.Peek() <= '9') { + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path break; else { @@ -884,19 +1594,19 @@ private: } } - d = (double)i64; + d = static_cast(i64); #else // Use double to store significand in 32-bit architecture - d = use64bit ? (double)i64 : (double)i; + d = static_cast(use64bit ? i64 : i); #endif useDouble = true; } - while (s.Peek() >= '0' && s.Peek() <= '9') { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { if (significandDigit < 17) { d = d * 10.0 + (s.TakePush() - '0'); --expFrac; - if (d > 0.0) + if (RAPIDJSON_LIKELY(d > 0.0)) significandDigit++; } else @@ -908,38 +1618,35 @@ private: // Parse exp = e [ minus / plus ] 1*DIGIT int exp = 0; - if (s.Peek() == 'e' || s.Peek() == 'E') { + if (Consume(s, 'e') || Consume(s, 'E')) { if (!useDouble) { - d = use64bit ? i64 : i; + d = static_cast(use64bit ? i64 : i); useDouble = true; } - s.Take(); bool expMinus = false; - if (s.Peek() == '+') - s.Take(); - else if (s.Peek() == '-') { - s.Take(); + if (Consume(s, '+')) + ; + else if (Consume(s, '-')) expMinus = true; - } - if (s.Peek() >= '0' && s.Peek() <= '9') { - exp = s.Take() - '0'; + if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = static_cast(s.Take() - '0'); if (expMinus) { - while (s.Peek() >= '0' && s.Peek() <= '9') { - exp = exp * 10 + (s.Take() - '0'); + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); if (exp >= 214748364) { // Issue #313: prevent overflow exponent - while (s.Peek() >= '0' && s.Peek() <= '9') // Consume the rest of exponent + while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent s.Take(); } } } else { // positive exp int maxExp = 308 - expFrac; - while (s.Peek() >= '0' && s.Peek() <= '9') { - exp = exp * 10 + (s.Take() - '0'); - if (exp > maxExp) - RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (RAPIDJSON_UNLIKELY(exp > maxExp)) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); } } } @@ -952,34 +1659,63 @@ private: // Finish parsing, call event according to the type of number. bool cont = true; - size_t length = s.Length(); - const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. - if (useDouble) { - int p = exp + expFrac; - if (parseFlags & kParseFullPrecisionFlag) - d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); - else - d = internal::StrtodNormalPrecision(d, p); - - cont = handler.Double(minus ? -d : d); - } - else { - if (use64bit) { - if (minus) - cont = handler.Int64(-(int64_t)i64); - else - cont = handler.Uint64(i64); + if (parseFlags & kParseNumbersAsStringsFlag) { + if (parseFlags & kParseInsituFlag) { + s.Pop(); // Pop stack no matter if it will be used or not. + typename InputStream::Ch* head = is.PutBegin(); + const size_t length = s.Tell() - startOffset; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + // unable to insert the \0 character here, it will erase the comma after this number + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + cont = handler.RawNumber(str, SizeType(length), false); } else { - if (minus) - cont = handler.Int(-(int)i); - else - cont = handler.Uint(i); + SizeType numCharsToCopy = static_cast(s.Length()); + StringStream srcStream(s.Pop()); + StackStream dstStream(stack_); + while (numCharsToCopy--) { + Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); + } + dstStream.Put('\0'); + const typename TargetEncoding::Ch* str = dstStream.Pop(); + const SizeType length = static_cast(dstStream.Length()) - 1; + cont = handler.RawNumber(str, SizeType(length), true); } } - if (!cont) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + else { + size_t length = s.Length(); + const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. + + if (useDouble) { + int p = exp + expFrac; + if (parseFlags & kParseFullPrecisionFlag) + d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); + else + d = internal::StrtodNormalPrecision(d, p); + + cont = handler.Double(minus ? -d : d); + } + else if (useNanOrInf) { + cont = handler.Double(d); + } + else { + if (use64bit) { + if (minus) + cont = handler.Int64(static_cast(~i64 + 1)); + else + cont = handler.Uint64(i64); + } + else { + if (minus) + cont = handler.Int(static_cast(~i + 1)); + else + cont = handler.Uint(i); + } + } + } + if (RAPIDJSON_UNLIKELY(!cont)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); } // Parse any JSON value @@ -992,7 +1728,10 @@ private: case '"': ParseString(is, handler); break; case '{': ParseObject(is, handler); break; case '[': ParseArray (is, handler); break; - default : ParseNumber(is, handler); + default : + ParseNumber(is, handler); + break; + } } @@ -1000,27 +1739,29 @@ private: // States enum IterativeParsingState { - IterativeParsingStartState = 0, - IterativeParsingFinishState, - IterativeParsingErrorState, + IterativeParsingFinishState = 0, // sink states at top + IterativeParsingErrorState, // sink states at top + IterativeParsingStartState, // Object states IterativeParsingObjectInitialState, IterativeParsingMemberKeyState, - IterativeParsingKeyValueDelimiterState, IterativeParsingMemberValueState, - IterativeParsingMemberDelimiterState, IterativeParsingObjectFinishState, // Array states IterativeParsingArrayInitialState, IterativeParsingElementState, - IterativeParsingElementDelimiterState, IterativeParsingArrayFinishState, // Single value state IterativeParsingValueState, - + + // Delimiter states (at bottom) + IterativeParsingElementDelimiterState, + IterativeParsingMemberDelimiterState, + IterativeParsingKeyValueDelimiterState, + cIterativeParsingStateCount }; @@ -1064,9 +1805,9 @@ private: #undef N #undef N16 //!@endcond - - if (sizeof(Ch) == 1 || unsigned(c) < 256) - return (Token)tokenMap[(unsigned char)c]; + + if (sizeof(Ch) == 1 || static_cast(c) < 256) + return static_cast(tokenMap[static_cast(c)]); else return NumberToken; } @@ -1074,6 +1815,18 @@ private: RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) { // current state x one lookahead token -> new state static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, // Start { IterativeParsingArrayInitialState, // Left bracket @@ -1088,18 +1841,6 @@ private: IterativeParsingValueState, // Null IterativeParsingValueState // Number }, - // Finish(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, - // Error(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, // ObjectInitial { IterativeParsingErrorState, // Left bracket @@ -1128,20 +1869,6 @@ private: IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, - // KeyValueDelimiter - { - IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) - IterativeParsingErrorState, // Right bracket - IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberValueState, // String - IterativeParsingMemberValueState, // False - IterativeParsingMemberValueState, // True - IterativeParsingMemberValueState, // Null - IterativeParsingMemberValueState // Number - }, // MemberValue { IterativeParsingErrorState, // Left bracket @@ -1156,20 +1883,6 @@ private: IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, - // MemberDelimiter - { - IterativeParsingErrorState, // Left bracket - IterativeParsingErrorState, // Right bracket - IterativeParsingErrorState, // Left curly bracket - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberKeyState, // String - IterativeParsingErrorState, // False - IterativeParsingErrorState, // True - IterativeParsingErrorState, // Null - IterativeParsingErrorState // Number - }, // ObjectFinish(sink state) { IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, @@ -1204,20 +1917,6 @@ private: IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, - // ElementDelimiter - { - IterativeParsingArrayInitialState, // Left bracket(push Element state) - IterativeParsingErrorState, // Right bracket - IterativeParsingObjectInitialState, // Left curly bracket(push Element state) - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingElementState, // String - IterativeParsingElementState, // False - IterativeParsingElementState, // True - IterativeParsingElementState, // Null - IterativeParsingElementState // Number - }, // ArrayFinish(sink state) { IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, @@ -1229,10 +1928,52 @@ private: IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState - } + }, + // ElementDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // MemberDelimiter + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // KeyValueDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, }; // End of G - return (IterativeParsingState)G[state][token]; + return static_cast(G[state][token]); } // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). @@ -1309,6 +2050,11 @@ private: case IterativeParsingObjectFinishState: { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell()); + return IterativeParsingErrorState; + } // Get member count. SizeType c = *stack_.template Pop(1); // If the object is not empty, count the last member. @@ -1334,6 +2080,11 @@ private: case IterativeParsingArrayFinishState: { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell()); + return IterativeParsingErrorState; + } // Get element count. SizeType c = *stack_.template Pop(1); // If the array is not empty, count the last element. @@ -1385,55 +2136,68 @@ private: // Error flag has been set. return; } - + switch (src) { - case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); - case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); + case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return; + case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return; case IterativeParsingObjectInitialState: - case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); - case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); - case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); - case IterativeParsingElementState: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); - default: RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); - } + case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return; + case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return; + case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return; + case IterativeParsingKeyValueDelimiterState: + case IterativeParsingArrayInitialState: + case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return; + default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return; + } } + RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) { + return s >= IterativeParsingElementDelimiterState; + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) { + return s <= IterativeParsingErrorState; + } + template ParseResult IterativeParse(InputStream& is, Handler& handler) { parseResult_.Clear(); ClearStackOnExit scope(*this); IterativeParsingState state = IterativeParsingStartState; - - SkipWhitespace(is); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); while (is.Peek() != '\0') { Token t = Tokenize(is.Peek()); IterativeParsingState n = Predict(state, t); IterativeParsingState d = Transit(state, t, n, is, handler); - + if (d == IterativeParsingErrorState) { HandleError(state, is); break; } - + state = d; - + // Do not further consume streams if a root JSON has been parsed. if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) break; - - SkipWhitespace(is); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); } - + // Handle the end of file. if (state != IterativeParsingFinishState) HandleError(state, is); - + return parseResult_; } static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. ParseResult parseResult_; + IterativeParsingState state_; }; // class GenericReader //! Reader with UTF8 encoding and default allocator. @@ -1441,6 +2205,11 @@ typedef GenericReader, UTF8<> > Reader; RAPIDJSON_NAMESPACE_END +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + + #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif diff --git a/contrib/rapidjson/include/rapidjson/schema.h b/contrib/rapidjson/include/rapidjson/schema.h new file mode 100644 index 000000000..abcf1a102 --- /dev/null +++ b/contrib/rapidjson/include/rapidjson/schema.h @@ -0,0 +1,2016 @@ +// Tencent is pleased to support the open source community by making RapidJSON available-> +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License-> You may obtain a copy of the License at +// +// http://opensource->org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied-> See the License for the +// specific language governing permissions and limitations under the License-> + +#ifndef RAPIDJSON_SCHEMA_H_ +#define RAPIDJSON_SCHEMA_H_ + +#include "document.h" +#include "pointer.h" +#include // abs, floor + +#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0 +#endif + +#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) +#define RAPIDJSON_SCHEMA_USE_STDREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_STDREGEX 0 +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX +#include "internal/regex.h" +#elif RAPIDJSON_SCHEMA_USE_STDREGEX +#include +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX +#define RAPIDJSON_SCHEMA_HAS_REGEX 1 +#else +#define RAPIDJSON_SCHEMA_HAS_REGEX 0 +#endif + +#ifndef RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_VERBOSE 0 +#endif + +#if RAPIDJSON_SCHEMA_VERBOSE +#include "stringbuffer.h" +#endif + +RAPIDJSON_DIAG_PUSH + +#if defined(__GNUC__) +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(weak-vtables) +RAPIDJSON_DIAG_OFF(exit-time-destructors) +RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) +RAPIDJSON_DIAG_OFF(variadic-macros) +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Verbose Utilities + +#if RAPIDJSON_SCHEMA_VERBOSE + +namespace internal { + +inline void PrintInvalidKeyword(const char* keyword) { + printf("Fail keyword: %s\n", keyword); +} + +inline void PrintInvalidKeyword(const wchar_t* keyword) { + wprintf(L"Fail keyword: %ls\n", keyword); +} + +inline void PrintInvalidDocument(const char* document) { + printf("Fail document: %s\n\n", document); +} + +inline void PrintInvalidDocument(const wchar_t* document) { + wprintf(L"Fail document: %ls\n\n", document); +} + +inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) { + printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d); +} + +inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) { + wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d); +} + +} // namespace internal + +#endif // RAPIDJSON_SCHEMA_VERBOSE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_INVALID_KEYWORD_RETURN + +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword) +#else +#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) +#endif + +#define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + context.invalidKeyword = keyword.GetString();\ + RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\ + return false;\ +RAPIDJSON_MULTILINEMACRO_END + +/////////////////////////////////////////////////////////////////////////////// +// Forward declarations + +template +class GenericSchemaDocument; + +namespace internal { + +template +class Schema; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaValidator + +class ISchemaValidator { +public: + virtual ~ISchemaValidator() {} + virtual bool IsValid() const = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaStateFactory + +template +class ISchemaStateFactory { +public: + virtual ~ISchemaStateFactory() {} + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0; + virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; + virtual void* CreateHasher() = 0; + virtual uint64_t GetHashCode(void* hasher) = 0; + virtual void DestroryHasher(void* hasher) = 0; + virtual void* MallocState(size_t size) = 0; + virtual void FreeState(void* p) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Hasher + +// For comparison of compound value +template +class Hasher { +public: + typedef typename Encoding::Ch Ch; + + Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {} + + bool Null() { return WriteType(kNullType); } + bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); } + bool Int(int i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Double(double d) { + Number n; + if (d < 0) n.u.i = static_cast(d); + else n.u.u = static_cast(d); + n.d = d; + return WriteNumber(n); + } + + bool RawNumber(const Ch* str, SizeType len, bool) { + WriteBuffer(kNumberType, str, len * sizeof(Ch)); + return true; + } + + bool String(const Ch* str, SizeType len, bool) { + WriteBuffer(kStringType, str, len * sizeof(Ch)); + return true; + } + + bool StartObject() { return true; } + bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } + bool EndObject(SizeType memberCount) { + uint64_t h = Hash(0, kObjectType); + uint64_t* kv = stack_.template Pop(memberCount * 2); + for (SizeType i = 0; i < memberCount; i++) + h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive + *stack_.template Push() = h; + return true; + } + + bool StartArray() { return true; } + bool EndArray(SizeType elementCount) { + uint64_t h = Hash(0, kArrayType); + uint64_t* e = stack_.template Pop(elementCount); + for (SizeType i = 0; i < elementCount; i++) + h = Hash(h, e[i]); // Use hash to achieve element order sensitive + *stack_.template Push() = h; + return true; + } + + bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); } + + uint64_t GetHashCode() const { + RAPIDJSON_ASSERT(IsValid()); + return *stack_.template Top(); + } + +private: + static const size_t kDefaultSize = 256; + struct Number { + union U { + uint64_t u; + int64_t i; + }u; + double d; + }; + + bool WriteType(Type type) { return WriteBuffer(type, 0, 0); } + + bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); } + + bool WriteBuffer(Type type, const void* data, size_t len) { + // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/ + uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type); + const unsigned char* d = static_cast(data); + for (size_t i = 0; i < len; i++) + h = Hash(h, d[i]); + *stack_.template Push() = h; + return true; + } + + static uint64_t Hash(uint64_t h, uint64_t d) { + static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3); + h ^= d; + h *= kPrime; + return h; + } + + Stack stack_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidationContext + +template +struct SchemaValidationContext { + typedef Schema SchemaType; + typedef ISchemaStateFactory SchemaValidatorFactoryType; + typedef typename SchemaType::ValueType ValueType; + typedef typename ValueType::Ch Ch; + + enum PatternValidatorType { + kPatternValidatorOnly, + kPatternValidatorWithProperty, + kPatternValidatorWithAdditionalProperty + }; + + SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) : + factory(f), + schema(s), + valueSchema(), + invalidKeyword(), + hasher(), + arrayElementHashCodes(), + validators(), + validatorCount(), + patternPropertiesValidators(), + patternPropertiesValidatorCount(), + patternPropertiesSchemas(), + patternPropertiesSchemaCount(), + valuePatternValidatorType(kPatternValidatorOnly), + propertyExist(), + inArray(false), + valueUniqueness(false), + arrayUniqueness(false) + { + } + + ~SchemaValidationContext() { + if (hasher) + factory.DestroryHasher(hasher); + if (validators) { + for (SizeType i = 0; i < validatorCount; i++) + factory.DestroySchemaValidator(validators[i]); + factory.FreeState(validators); + } + if (patternPropertiesValidators) { + for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) + factory.DestroySchemaValidator(patternPropertiesValidators[i]); + factory.FreeState(patternPropertiesValidators); + } + if (patternPropertiesSchemas) + factory.FreeState(patternPropertiesSchemas); + if (propertyExist) + factory.FreeState(propertyExist); + } + + SchemaValidatorFactoryType& factory; + const SchemaType* schema; + const SchemaType* valueSchema; + const Ch* invalidKeyword; + void* hasher; // Only validator access + void* arrayElementHashCodes; // Only validator access this + ISchemaValidator** validators; + SizeType validatorCount; + ISchemaValidator** patternPropertiesValidators; + SizeType patternPropertiesValidatorCount; + const SchemaType** patternPropertiesSchemas; + SizeType patternPropertiesSchemaCount; + PatternValidatorType valuePatternValidatorType; + PatternValidatorType objectPatternValidatorType; + SizeType arrayElementIndex; + bool* propertyExist; + bool inArray; + bool valueUniqueness; + bool arrayUniqueness; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Schema + +template +class Schema { +public: + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef SchemaValidationContext Context; + typedef Schema SchemaType; + typedef GenericValue SValue; + friend class GenericSchemaDocument; + + Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : + allocator_(allocator), + typeless_(schemaDocument->GetTypeless()), + enum_(), + enumCount_(), + not_(), + type_((1 << kTotalSchemaType) - 1), // typeless + validatorCount_(), + properties_(), + additionalPropertiesSchema_(), + patternProperties_(), + patternPropertyCount_(), + propertyCount_(), + minProperties_(), + maxProperties_(SizeType(~0)), + additionalProperties_(true), + hasDependencies_(), + hasRequired_(), + hasSchemaDependencies_(), + additionalItemsSchema_(), + itemsList_(), + itemsTuple_(), + itemsTupleCount_(), + minItems_(), + maxItems_(SizeType(~0)), + additionalItems_(true), + uniqueItems_(false), + pattern_(), + minLength_(0), + maxLength_(~SizeType(0)), + exclusiveMinimum_(false), + exclusiveMaximum_(false) + { + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename ValueType::ConstValueIterator ConstValueIterator; + typedef typename ValueType::ConstMemberIterator ConstMemberIterator; + + if (!value.IsObject()) + return; + + if (const ValueType* v = GetMember(value, GetTypeString())) { + type_ = 0; + if (v->IsString()) + AddType(*v); + else if (v->IsArray()) + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) + AddType(*itr); + } + + if (const ValueType* v = GetMember(value, GetEnumString())) + if (v->IsArray() && v->Size() > 0) { + enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { + typedef Hasher > EnumHasherType; + char buffer[256 + 24]; + MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer)); + EnumHasherType h(&hasherAllocator, 256); + itr->Accept(h); + enum_[enumCount_++] = h.GetHashCode(); + } + } + + if (schemaDocument) { + AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); + AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); + AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); + } + + if (const ValueType* v = GetMember(value, GetNotString())) { + schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document); + notValidatorIndex_ = validatorCount_; + validatorCount_++; + } + + // Object + + const ValueType* properties = GetMember(value, GetPropertiesString()); + const ValueType* required = GetMember(value, GetRequiredString()); + const ValueType* dependencies = GetMember(value, GetDependenciesString()); + { + // Gather properties from properties/required/dependencies + SValue allProperties(kArrayType); + + if (properties && properties->IsObject()) + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) + AddUniqueElement(allProperties, itr->name); + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) + AddUniqueElement(allProperties, *itr); + + if (dependencies && dependencies->IsObject()) + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + AddUniqueElement(allProperties, itr->name); + if (itr->value.IsArray()) + for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i) + if (i->IsString()) + AddUniqueElement(allProperties, *i); + } + + if (allProperties.Size() > 0) { + propertyCount_ = allProperties.Size(); + properties_ = static_cast(allocator_->Malloc(sizeof(Property) * propertyCount_)); + for (SizeType i = 0; i < propertyCount_; i++) { + new (&properties_[i]) Property(); + properties_[i].name = allProperties[i]; + properties_[i].schema = typeless_; + } + } + } + + if (properties && properties->IsObject()) { + PointerType q = p.Append(GetPropertiesString(), allocator_); + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { + SizeType index; + if (FindPropertyIndex(itr->name, &index)) + schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document); + } + } + + if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { + PointerType q = p.Append(GetPatternPropertiesString(), allocator_); + patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); + patternPropertyCount_ = 0; + + for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { + new (&patternProperties_[patternPropertyCount_]) PatternProperty(); + patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name); + schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document); + patternPropertyCount_++; + } + } + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) { + SizeType index; + if (FindPropertyIndex(*itr, &index)) { + properties_[index].required = true; + hasRequired_ = true; + } + } + + if (dependencies && dependencies->IsObject()) { + PointerType q = p.Append(GetDependenciesString(), allocator_); + hasDependencies_ = true; + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + SizeType sourceIndex; + if (FindPropertyIndex(itr->name, &sourceIndex)) { + if (itr->value.IsArray()) { + properties_[sourceIndex].dependencies = static_cast(allocator_->Malloc(sizeof(bool) * propertyCount_)); + std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_); + for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) { + SizeType targetIndex; + if (FindPropertyIndex(*targetItr, &targetIndex)) + properties_[sourceIndex].dependencies[targetIndex] = true; + } + } + else if (itr->value.IsObject()) { + hasSchemaDependencies_ = true; + schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document); + properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; + validatorCount_++; + } + } + } + } + + if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) { + if (v->IsBool()) + additionalProperties_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document); + } + + AssignIfExist(minProperties_, value, GetMinPropertiesString()); + AssignIfExist(maxProperties_, value, GetMaxPropertiesString()); + + // Array + if (const ValueType* v = GetMember(value, GetItemsString())) { + PointerType q = p.Append(GetItemsString(), allocator_); + if (v->IsObject()) // List validation + schemaDocument->CreateSchema(&itemsList_, q, *v, document); + else if (v->IsArray()) { // Tuple validation + itemsTuple_ = static_cast(allocator_->Malloc(sizeof(const Schema*) * v->Size())); + SizeType index = 0; + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++) + schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document); + } + } + + AssignIfExist(minItems_, value, GetMinItemsString()); + AssignIfExist(maxItems_, value, GetMaxItemsString()); + + if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { + if (v->IsBool()) + additionalItems_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document); + } + + AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); + + // String + AssignIfExist(minLength_, value, GetMinLengthString()); + AssignIfExist(maxLength_, value, GetMaxLengthString()); + + if (const ValueType* v = GetMember(value, GetPatternString())) + pattern_ = CreatePattern(*v); + + // Number + if (const ValueType* v = GetMember(value, GetMinimumString())) + if (v->IsNumber()) + minimum_.CopyFrom(*v, *allocator_); + + if (const ValueType* v = GetMember(value, GetMaximumString())) + if (v->IsNumber()) + maximum_.CopyFrom(*v, *allocator_); + + AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString()); + AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString()); + + if (const ValueType* v = GetMember(value, GetMultipleOfString())) + if (v->IsNumber() && v->GetDouble() > 0.0) + multipleOf_.CopyFrom(*v, *allocator_); + } + + ~Schema() { + AllocatorType::Free(enum_); + if (properties_) { + for (SizeType i = 0; i < propertyCount_; i++) + properties_[i].~Property(); + AllocatorType::Free(properties_); + } + if (patternProperties_) { + for (SizeType i = 0; i < patternPropertyCount_; i++) + patternProperties_[i].~PatternProperty(); + AllocatorType::Free(patternProperties_); + } + AllocatorType::Free(itemsTuple_); +#if RAPIDJSON_SCHEMA_HAS_REGEX + if (pattern_) { + pattern_->~RegexType(); + AllocatorType::Free(pattern_); + } +#endif + } + + bool BeginValue(Context& context) const { + if (context.inArray) { + if (uniqueItems_) + context.valueUniqueness = true; + + if (itemsList_) + context.valueSchema = itemsList_; + else if (itemsTuple_) { + if (context.arrayElementIndex < itemsTupleCount_) + context.valueSchema = itemsTuple_[context.arrayElementIndex]; + else if (additionalItemsSchema_) + context.valueSchema = additionalItemsSchema_; + else if (additionalItems_) + context.valueSchema = typeless_; + else + RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString()); + } + else + context.valueSchema = typeless_; + + context.arrayElementIndex++; + } + return true; + } + + RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { + if (context.patternPropertiesValidatorCount > 0) { + bool otherValid = false; + SizeType count = context.patternPropertiesValidatorCount; + if (context.objectPatternValidatorType != Context::kPatternValidatorOnly) + otherValid = context.patternPropertiesValidators[--count]->IsValid(); + + bool patternValid = true; + for (SizeType i = 0; i < count; i++) + if (!context.patternPropertiesValidators[i]->IsValid()) { + patternValid = false; + break; + } + + if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { + if (!patternValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { + if (!patternValid || !otherValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + + if (enum_) { + const uint64_t h = context.factory.GetHashCode(context.hasher); + for (SizeType i = 0; i < enumCount_; i++) + if (enum_[i] == h) + goto foundEnum; + RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString()); + foundEnum:; + } + + if (allOf_.schemas) + for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) + if (!context.validators[i]->IsValid()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString()); + + if (anyOf_.schemas) { + for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) + if (context.validators[i]->IsValid()) + goto foundAny; + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString()); + foundAny:; + } + + if (oneOf_.schemas) { + bool oneValid = false; + for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) + if (context.validators[i]->IsValid()) { + if (oneValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); + else + oneValid = true; + } + if (!oneValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); + } + + if (not_ && context.validators[notValidatorIndex_]->IsValid()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString()); + + return true; + } + + bool Null(Context& context) const { + if (!(type_ & (1 << kNullSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + return CreateParallelValidator(context); + } + + bool Bool(Context& context, bool) const { + if (!(type_ & (1 << kBooleanSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + return CreateParallelValidator(context); + } + + bool Int(Context& context, int i) const { + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint(Context& context, unsigned u) const { + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Int64(Context& context, int64_t i) const { + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint64(Context& context, uint64_t u) const { + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Double(Context& context, double d) const { + if (!(type_ & (1 << kNumberSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) + return false; + + if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) + return false; + + if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) + return false; + + return CreateParallelValidator(context); + } + + bool String(Context& context, const Ch* str, SizeType length, bool) const { + if (!(type_ & (1 << kStringSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (minLength_ != 0 || maxLength_ != SizeType(~0)) { + SizeType count; + if (internal::CountStringCodePoint(str, length, &count)) { + if (count < minLength_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString()); + if (count > maxLength_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString()); + } + } + + if (pattern_ && !IsPatternMatch(pattern_, str, length)) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString()); + + return CreateParallelValidator(context); + } + + bool StartObject(Context& context) const { + if (!(type_ & (1 << kObjectSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (hasDependencies_ || hasRequired_) { + context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); + std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_); + } + + if (patternProperties_) { // pre-allocate schema array + SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType + context.patternPropertiesSchemas = static_cast(context.factory.MallocState(sizeof(const SchemaType*) * count)); + context.patternPropertiesSchemaCount = 0; + std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count); + } + + return CreateParallelValidator(context); + } + + bool Key(Context& context, const Ch* str, SizeType len, bool) const { + if (patternProperties_) { + context.patternPropertiesSchemaCount = 0; + for (SizeType i = 0; i < patternPropertyCount_; i++) + if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; + context.valueSchema = typeless_; + } + } + + SizeType index; + if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { + if (context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; + context.valueSchema = typeless_; + context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; + } + else + context.valueSchema = properties_[index].schema; + + if (context.propertyExist) + context.propertyExist[index] = true; + + return true; + } + + if (additionalPropertiesSchema_) { + if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; + context.valueSchema = typeless_; + context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; + } + else + context.valueSchema = additionalPropertiesSchema_; + return true; + } + else if (additionalProperties_) { + context.valueSchema = typeless_; + return true; + } + + if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString()); + + return true; + } + + bool EndObject(Context& context, SizeType memberCount) const { + if (hasRequired_) + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].required) + if (!context.propertyExist[index]) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); + + if (memberCount < minProperties_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); + + if (memberCount > maxProperties_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString()); + + if (hasDependencies_) { + for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) + if (context.propertyExist[sourceIndex]) { + if (properties_[sourceIndex].dependencies) { + for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) + if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex]) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); + } + else if (properties_[sourceIndex].dependenciesSchema) + if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); + } + } + + return true; + } + + bool StartArray(Context& context) const { + if (!(type_ & (1 << kArraySchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + context.arrayElementIndex = 0; + context.inArray = true; + + return CreateParallelValidator(context); + } + + bool EndArray(Context& context, SizeType elementCount) const { + context.inArray = false; + + if (elementCount < minItems_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString()); + + if (elementCount > maxItems_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString()); + + return true; + } + + // Generate functions for string literal according to Ch +#define RAPIDJSON_STRING_(name, ...) \ + static const ValueType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const ValueType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1));\ + return v;\ + } + + RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') + RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n') + RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't') + RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y') + RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g') + RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r') + RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r') + RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e') + RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm') + RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f') + RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f') + RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f') + RAPIDJSON_STRING_(Not, 'n', 'o', 't') + RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd') + RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's') + RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n') + RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') + +#undef RAPIDJSON_STRING_ + +private: + enum SchemaValueType { + kNullSchemaType, + kBooleanSchemaType, + kObjectSchemaType, + kArraySchemaType, + kStringSchemaType, + kNumberSchemaType, + kIntegerSchemaType, + kTotalSchemaType + }; + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + typedef internal::GenericRegex RegexType; +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + typedef std::basic_regex RegexType; +#else + typedef char RegexType; +#endif + + struct SchemaArray { + SchemaArray() : schemas(), count() {} + ~SchemaArray() { AllocatorType::Free(schemas); } + const SchemaType** schemas; + SizeType begin; // begin index of context.validators + SizeType count; + }; + + template + void AddUniqueElement(V1& a, const V2& v) { + for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) + if (*itr == v) + return; + V1 c(v, *allocator_); + a.PushBack(c, *allocator_); + } + + static const ValueType* GetMember(const ValueType& value, const ValueType& name) { + typename ValueType::ConstMemberIterator itr = value.FindMember(name); + return itr != value.MemberEnd() ? &(itr->value) : 0; + } + + static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsBool()) + out = v->GetBool(); + } + + static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsUint64() && v->GetUint64() <= SizeType(~0)) + out = static_cast(v->GetUint64()); + } + + void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) { + if (const ValueType* v = GetMember(value, name)) { + if (v->IsArray() && v->Size() > 0) { + PointerType q = p.Append(name, allocator_); + out.count = v->Size(); + out.schemas = static_cast(allocator_->Malloc(out.count * sizeof(const Schema*))); + memset(out.schemas, 0, sizeof(Schema*)* out.count); + for (SizeType i = 0; i < out.count; i++) + schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document); + out.begin = validatorCount_; + validatorCount_ += out.count; + } + } + } + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) { + RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString()); + if (!r->IsValid()) { + r->~RegexType(); + AllocatorType::Free(r); + r = 0; + } + return r; + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { + GenericRegexSearch rs(*pattern); + return rs.Search(str); + } +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) + try { + return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); + } + catch (const std::regex_error&) { + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) { + std::match_results r; + return std::regex_search(str, str + length, r, *pattern); + } +#else + template + RegexType* CreatePattern(const ValueType&) { return 0; } + + static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } +#endif // RAPIDJSON_SCHEMA_USE_STDREGEX + + void AddType(const ValueType& type) { + if (type == GetNullString() ) type_ |= 1 << kNullSchemaType; + else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType; + else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType; + else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType; + else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType; + else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType; + else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); + } + + bool CreateParallelValidator(Context& context) const { + if (enum_ || context.arrayUniqueness) + context.hasher = context.factory.CreateHasher(); + + if (validatorCount_) { + RAPIDJSON_ASSERT(context.validators == 0); + context.validators = static_cast(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); + context.validatorCount = validatorCount_; + + if (allOf_.schemas) + CreateSchemaValidators(context, allOf_); + + if (anyOf_.schemas) + CreateSchemaValidators(context, anyOf_); + + if (oneOf_.schemas) + CreateSchemaValidators(context, oneOf_); + + if (not_) + context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_); + + if (hasSchemaDependencies_) { + for (SizeType i = 0; i < propertyCount_; i++) + if (properties_[i].dependenciesSchema) + context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema); + } + } + + return true; + } + + void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const { + for (SizeType i = 0; i < schemas.count; i++) + context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]); + } + + // O(n) + bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const { + SizeType len = name.GetStringLength(); + const Ch* str = name.GetString(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].name.GetStringLength() == len && + (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) + { + *outIndex = index; + return true; + } + return false; + } + + bool CheckInt(Context& context, int64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull()) { + if (minimum_.IsInt64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } + else if (minimum_.IsUint64()) { + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64() + } + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsInt64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } + else if (maximum_.IsUint64()) { } + /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckUint(Context& context, uint64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull()) { + if (minimum_.IsUint64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } + else if (minimum_.IsInt64()) + /* do nothing */; // i >= 0 > minimum.Getint64() + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsUint64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } + else if (maximum_.IsInt64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_ + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (i % multipleOf_.GetUint64() != 0) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckDoubleMinimum(Context& context, double d) const { + if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + return true; + } + + bool CheckDoubleMaximum(Context& context, double d) const { + if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + return true; + } + + bool CheckDoubleMultipleOf(Context& context, double d) const { + double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); + double q = std::floor(a / b); + double r = a - q * b; + if (r > 0.0) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + return true; + } + + struct Property { + Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} + ~Property() { AllocatorType::Free(dependencies); } + SValue name; + const SchemaType* schema; + const SchemaType* dependenciesSchema; + SizeType dependenciesValidatorIndex; + bool* dependencies; + bool required; + }; + + struct PatternProperty { + PatternProperty() : schema(), pattern() {} + ~PatternProperty() { + if (pattern) { + pattern->~RegexType(); + AllocatorType::Free(pattern); + } + } + const SchemaType* schema; + RegexType* pattern; + }; + + AllocatorType* allocator_; + const SchemaType* typeless_; + uint64_t* enum_; + SizeType enumCount_; + SchemaArray allOf_; + SchemaArray anyOf_; + SchemaArray oneOf_; + const SchemaType* not_; + unsigned type_; // bitmask of kSchemaType + SizeType validatorCount_; + SizeType notValidatorIndex_; + + Property* properties_; + const SchemaType* additionalPropertiesSchema_; + PatternProperty* patternProperties_; + SizeType patternPropertyCount_; + SizeType propertyCount_; + SizeType minProperties_; + SizeType maxProperties_; + bool additionalProperties_; + bool hasDependencies_; + bool hasRequired_; + bool hasSchemaDependencies_; + + const SchemaType* additionalItemsSchema_; + const SchemaType* itemsList_; + const SchemaType** itemsTuple_; + SizeType itemsTupleCount_; + SizeType minItems_; + SizeType maxItems_; + bool additionalItems_; + bool uniqueItems_; + + RegexType* pattern_; + SizeType minLength_; + SizeType maxLength_; + + SValue minimum_; + SValue maximum_; + SValue multipleOf_; + bool exclusiveMinimum_; + bool exclusiveMaximum_; +}; + +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + *documentStack.template Push() = '/'; + char buffer[21]; + size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); + for (size_t i = 0; i < length; i++) + *documentStack.template Push() = static_cast(buffer[i]); + } +}; + +// Partial specialized version for char to prevent buffer copying. +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + if (sizeof(SizeType) == 4) { + char *buffer = documentStack.template Push(1 + 10); // '/' + uint + *buffer++ = '/'; + const char* end = internal::u32toa(index, buffer); + documentStack.template Pop(static_cast(10 - (end - buffer))); + } + else { + char *buffer = documentStack.template Push(1 + 20); // '/' + uint64 + *buffer++ = '/'; + const char* end = internal::u64toa(index, buffer); + documentStack.template Pop(static_cast(20 - (end - buffer))); + } + } +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// IGenericRemoteSchemaDocumentProvider + +template +class IGenericRemoteSchemaDocumentProvider { +public: + typedef typename SchemaDocumentType::Ch Ch; + + virtual ~IGenericRemoteSchemaDocumentProvider() {} + virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaDocument + +//! JSON schema document. +/*! + A JSON schema document is a compiled version of a JSON schema. + It is basically a tree of internal::Schema. + + \note This is an immutable class (i.e. its instance cannot be modified after construction). + \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding. + \tparam Allocator Allocator type for allocating memory of this document. +*/ +template +class GenericSchemaDocument { +public: + typedef ValueT ValueType; + typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProviderType; + typedef Allocator AllocatorType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef internal::Schema SchemaType; + typedef GenericPointer PointerType; + friend class internal::Schema; + template + friend class GenericSchemaValidator; + + //! Constructor. + /*! + Compile a JSON document into schema document. + + \param document A JSON document as source. + \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. + \param allocator An optional allocator instance for allocating memory. Can be null. + */ + explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : + remoteProvider_(remoteProvider), + allocator_(allocator), + ownAllocator_(), + root_(), + typeless_(), + schemaMap_(allocator, kInitialSchemaMapSize), + schemaRef_(allocator, kInitialSchemaRefSize) + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); + new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0); + + // Generate root schema, it will call CreateSchema() to create sub-schemas, + // And call AddRefSchema() if there are $ref. + CreateSchemaRecursive(&root_, PointerType(), document, document); + + // Resolve $ref + while (!schemaRef_.Empty()) { + SchemaRefEntry* refEntry = schemaRef_.template Pop(1); + if (const SchemaType* s = GetSchema(refEntry->target)) { + if (refEntry->schema) + *refEntry->schema = s; + + // Create entry in map if not exist + if (!GetSchema(refEntry->source)) { + new (schemaMap_.template Push()) SchemaEntry(refEntry->source, const_cast(s), false, allocator_); + } + } + else if (refEntry->schema) + *refEntry->schema = typeless_; + + refEntry->~SchemaRefEntry(); + } + + RAPIDJSON_ASSERT(root_ != 0); + + schemaRef_.ShrinkToFit(); // Deallocate all memory for ref + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT : + remoteProvider_(rhs.remoteProvider_), + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + root_(rhs.root_), + typeless_(rhs.typeless_), + schemaMap_(std::move(rhs.schemaMap_)), + schemaRef_(std::move(rhs.schemaRef_)) + { + rhs.remoteProvider_ = 0; + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.typeless_ = 0; + } +#endif + + //! Destructor + ~GenericSchemaDocument() { + while (!schemaMap_.Empty()) + schemaMap_.template Pop(1)->~SchemaEntry(); + + if (typeless_) { + typeless_->~SchemaType(); + Allocator::Free(typeless_); + } + + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Get the root schema. + const SchemaType& GetRoot() const { return *root_; } + +private: + //! Prohibit copying + GenericSchemaDocument(const GenericSchemaDocument&); + //! Prohibit assignment + GenericSchemaDocument& operator=(const GenericSchemaDocument&); + + struct SchemaRefEntry { + SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {} + PointerType source; + PointerType target; + const SchemaType** schema; + }; + + struct SchemaEntry { + SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} + ~SchemaEntry() { + if (owned) { + schema->~SchemaType(); + Allocator::Free(schema); + } + } + PointerType pointer; + SchemaType* schema; + bool owned; + }; + + void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + if (schema) + *schema = typeless_; + + if (v.GetType() == kObjectType) { + const SchemaType* s = GetSchema(pointer); + if (!s) + CreateSchema(schema, pointer, v, document); + + for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) + CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document); + } + else if (v.GetType() == kArrayType) + for (SizeType i = 0; i < v.Size(); i++) + CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document); + } + + void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + RAPIDJSON_ASSERT(pointer.IsValid()); + if (v.IsObject()) { + if (!HandleRefSchema(pointer, schema, v, document)) { + SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_); + new (schemaMap_.template Push()) SchemaEntry(pointer, s, true, allocator_); + if (schema) + *schema = s; + } + } + } + + bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) { + static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' }; + static const ValueType kRefValue(kRefString, 4); + + typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue); + if (itr == v.MemberEnd()) + return false; + + if (itr->value.IsString()) { + SizeType len = itr->value.GetStringLength(); + if (len > 0) { + const Ch* s = itr->value.GetString(); + SizeType i = 0; + while (i < len && s[i] != '#') // Find the first # + i++; + + if (i > 0) { // Remote reference, resolve immediately + if (remoteProvider_) { + if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) { + PointerType pointer(&s[i], len - i, allocator_); + if (pointer.IsValid()) { + if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) { + if (schema) + *schema = sc; + return true; + } + } + } + } + } + else if (s[i] == '#') { // Local reference, defer resolution + PointerType pointer(&s[i], len - i, allocator_); + if (pointer.IsValid()) { + if (const ValueType* nv = pointer.Get(document)) + if (HandleRefSchema(source, schema, *nv, document)) + return true; + + new (schemaRef_.template Push()) SchemaRefEntry(source, pointer, schema, allocator_); + return true; + } + } + } + } + return false; + } + + const SchemaType* GetSchema(const PointerType& pointer) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (pointer == target->pointer) + return target->schema; + return 0; + } + + PointerType GetPointer(const SchemaType* schema) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (schema == target->schema) + return target->pointer; + return PointerType(); + } + + const SchemaType* GetTypeless() const { return typeless_; } + + static const size_t kInitialSchemaMapSize = 64; + static const size_t kInitialSchemaRefSize = 64; + + IRemoteSchemaDocumentProviderType* remoteProvider_; + Allocator *allocator_; + Allocator *ownAllocator_; + const SchemaType* root_; //!< Root schema. + SchemaType* typeless_; + internal::Stack schemaMap_; // Stores created Pointer -> Schemas + internal::Stack schemaRef_; // Stores Pointer from $ref and schema which holds the $ref +}; + +//! GenericSchemaDocument using Value type. +typedef GenericSchemaDocument SchemaDocument; +//! IGenericRemoteSchemaDocumentProvider using SchemaDocument. +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaValidator + +//! JSON Schema Validator. +/*! + A SAX style JSON schema validator. + It uses a \c GenericSchemaDocument to validate SAX events. + It delegates the incoming SAX events to an output handler. + The default output handler does nothing. + It can be reused multiple times by calling \c Reset(). + + \tparam SchemaDocumentType Type of schema document. + \tparam OutputHandler Type of output handler. Default handler does nothing. + \tparam StateAllocator Allocator for storing the internal validation states. +*/ +template < + typename SchemaDocumentType, + typename OutputHandler = BaseReaderHandler, + typename StateAllocator = CrtAllocator> +class GenericSchemaValidator : + public internal::ISchemaStateFactory, + public internal::ISchemaValidator +{ +public: + typedef typename SchemaDocumentType::SchemaType SchemaType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename SchemaType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + + //! Constructor without output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + valid_(true) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(0) +#endif + { + } + + //! Constructor with output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + OutputHandler& outputHandler, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(&outputHandler), + valid_(true) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(0) +#endif + { + } + + //! Destructor. + ~GenericSchemaValidator() { + Reset(); + RAPIDJSON_DELETE(ownStateAllocator_); + } + + //! Reset the internal states. + void Reset() { + while (!schemaStack_.Empty()) + PopSchema(); + documentStack_.Clear(); + valid_ = true; + } + + //! Checks whether the current state is valid. + // Implementation of ISchemaValidator + virtual bool IsValid() const { return valid_; } + + //! Gets the JSON pointer pointed to the invalid schema. + PointerType GetInvalidSchemaPointer() const { + return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema()); + } + + //! Gets the keyword of invalid schema. + const Ch* GetInvalidSchemaKeyword() const { + return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword; + } + + //! Gets the JSON pointer pointed to the invalid value. + PointerType GetInvalidDocumentPointer() const { + return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + } + +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + *documentStack_.template Push() = '\0';\ + documentStack_.template Pop(1);\ + internal::PrintInvalidDocument(documentStack_.template Bottom());\ +RAPIDJSON_MULTILINEMACRO_END +#else +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() +#endif + +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ + if (!valid_) return false; \ + if (!BeginValue() || !CurrentSchema().method arg1) {\ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\ + return valid_ = false;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ + for (Context* context = schemaStack_.template Bottom(); context != schemaStack_.template End(); context++) {\ + if (context->hasher)\ + static_cast(context->hasher)->method arg2;\ + if (context->validators)\ + for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\ + static_cast(context->validators[i_])->method arg2;\ + if (context->patternPropertiesValidators)\ + for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\ + static_cast(context->patternPropertiesValidators[i_])->method arg2;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ + return valid_ = EndValue() && (!outputHandler_ || outputHandler_->method arg2) + +#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ + RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) + + bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); } + bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } + bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } + bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } + bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } + bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } + bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } + bool RawNumber(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + bool String(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + + bool StartObject() { + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); + return valid_ = !outputHandler_ || outputHandler_->StartObject(); + } + + bool Key(const Ch* str, SizeType len, bool copy) { + if (!valid_) return false; + AppendToken(str, len); + if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); + return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); + } + + bool EndObject(SizeType memberCount) { + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); + if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); + } + + bool StartArray() { + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); + return valid_ = !outputHandler_ || outputHandler_->StartArray(); + } + + bool EndArray(SizeType elementCount) { + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); + if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); + } + +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_ +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ +#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ +#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ + + // Implementation of ISchemaStateFactory + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) { + return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, +#if RAPIDJSON_SCHEMA_VERBOSE + depth_ + 1, +#endif + &GetStateAllocator()); + } + + virtual void DestroySchemaValidator(ISchemaValidator* validator) { + GenericSchemaValidator* v = static_cast(validator); + v->~GenericSchemaValidator(); + StateAllocator::Free(v); + } + + virtual void* CreateHasher() { + return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator()); + } + + virtual uint64_t GetHashCode(void* hasher) { + return static_cast(hasher)->GetHashCode(); + } + + virtual void DestroryHasher(void* hasher) { + HasherType* h = static_cast(hasher); + h->~HasherType(); + StateAllocator::Free(h); + } + + virtual void* MallocState(size_t size) { + return GetStateAllocator().Malloc(size); + } + + virtual void FreeState(void* p) { + StateAllocator::Free(p); + } + +private: + typedef typename SchemaType::Context Context; + typedef GenericValue, StateAllocator> HashCodeArray; + typedef internal::Hasher HasherType; + + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + const SchemaType& root, +#if RAPIDJSON_SCHEMA_VERBOSE + unsigned depth, +#endif + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(root), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + valid_(true) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(depth) +#endif + { + } + + StateAllocator& GetStateAllocator() { + if (!stateAllocator_) + stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)(); + return *stateAllocator_; + } + + bool BeginValue() { + if (schemaStack_.Empty()) + PushSchema(root_); + else { + if (CurrentContext().inArray) + internal::TokenHelper, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); + + if (!CurrentSchema().BeginValue(CurrentContext())) + return false; + + SizeType count = CurrentContext().patternPropertiesSchemaCount; + const SchemaType** sa = CurrentContext().patternPropertiesSchemas; + typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; + bool valueUniqueness = CurrentContext().valueUniqueness; + RAPIDJSON_ASSERT(CurrentContext().valueSchema); + PushSchema(*CurrentContext().valueSchema); + + if (count > 0) { + CurrentContext().objectPatternValidatorType = patternValidatorType; + ISchemaValidator**& va = CurrentContext().patternPropertiesValidators; + SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount; + va = static_cast(MallocState(sizeof(ISchemaValidator*) * count)); + for (SizeType i = 0; i < count; i++) + va[validatorCount++] = CreateSchemaValidator(*sa[i]); + } + + CurrentContext().arrayUniqueness = valueUniqueness; + } + return true; + } + + bool EndValue() { + if (!CurrentSchema().EndValue(CurrentContext())) + return false; + +#if RAPIDJSON_SCHEMA_VERBOSE + GenericStringBuffer sb; + schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb); + + *documentStack_.template Push() = '\0'; + documentStack_.template Pop(1); + internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom()); +#endif + + uint64_t h = CurrentContext().arrayUniqueness ? static_cast(CurrentContext().hasher)->GetHashCode() : 0; + + PopSchema(); + + if (!schemaStack_.Empty()) { + Context& context = CurrentContext(); + if (context.valueUniqueness) { + HashCodeArray* a = static_cast(context.arrayElementHashCodes); + if (!a) + CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); + for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) + if (itr->GetUint64() == h) + RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString()); + a->PushBack(h, GetStateAllocator()); + } + } + + // Remove the last token of document pointer + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') + ; + + return true; + } + + void AppendToken(const Ch* str, SizeType len) { + documentStack_.template Reserve(1 + len * 2); // worst case all characters are escaped as two characters + *documentStack_.template PushUnsafe() = '/'; + for (SizeType i = 0; i < len; i++) { + if (str[i] == '~') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '0'; + } + else if (str[i] == '/') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '1'; + } + else + *documentStack_.template PushUnsafe() = str[i]; + } + } + + RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, &schema); } + + RAPIDJSON_FORCEINLINE void PopSchema() { + Context* c = schemaStack_.template Pop(1); + if (HashCodeArray* a = static_cast(c->arrayElementHashCodes)) { + a->~HashCodeArray(); + StateAllocator::Free(a); + } + c->~Context(); + } + + const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } + Context& CurrentContext() { return *schemaStack_.template Top(); } + const Context& CurrentContext() const { return *schemaStack_.template Top(); } + + static const size_t kDefaultSchemaStackCapacity = 1024; + static const size_t kDefaultDocumentStackCapacity = 256; + const SchemaDocumentType* schemaDocument_; + const SchemaType& root_; + StateAllocator* stateAllocator_; + StateAllocator* ownStateAllocator_; + internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) + internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) + OutputHandler* outputHandler_; + bool valid_; +#if RAPIDJSON_SCHEMA_VERBOSE + unsigned depth_; +#endif +}; + +typedef GenericSchemaValidator SchemaValidator; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidatingReader + +//! A helper class for parsing with validation. +/*! + This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate(). + + \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam SourceEncoding Encoding of the input stream. + \tparam SchemaDocumentType Type of schema document. + \tparam StackAllocator Allocator type for stack. +*/ +template < + unsigned parseFlags, + typename InputStream, + typename SourceEncoding, + typename SchemaDocumentType = SchemaDocument, + typename StackAllocator = CrtAllocator> +class SchemaValidatingReader { +public: + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename InputStream::Ch Ch; + + //! Constructor + /*! + \param is Input stream. + \param sd Schema document. + */ + SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {} + + template + bool operator()(Handler& handler) { + GenericReader reader; + GenericSchemaValidator validator(sd_, handler); + parseResult_ = reader.template Parse(is_, validator); + + isValid_ = validator.IsValid(); + if (isValid_) { + invalidSchemaPointer_ = PointerType(); + invalidSchemaKeyword_ = 0; + invalidDocumentPointer_ = PointerType(); + } + else { + invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); + invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); + invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); + } + + return parseResult_; + } + + const ParseResult& GetParseResult() const { return parseResult_; } + bool IsValid() const { return isValid_; } + const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } + const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } + const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } + +private: + InputStream& is_; + const SchemaDocumentType& sd_; + + ParseResult parseResult_; + PointerType invalidSchemaPointer_; + const Ch* invalidSchemaKeyword_; + PointerType invalidDocumentPointer_; + bool isValid_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#endif // RAPIDJSON_SCHEMA_H_ diff --git a/contrib/rapidjson/include/rapidjson/stream.h b/contrib/rapidjson/include/rapidjson/stream.h new file mode 100644 index 000000000..fef82c252 --- /dev/null +++ b/contrib/rapidjson/include/rapidjson/stream.h @@ -0,0 +1,179 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#include "rapidjson.h" + +#ifndef RAPIDJSON_STREAM_H_ +#define RAPIDJSON_STREAM_H_ + +#include "encodings.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Stream + +/*! \class rapidjson::Stream + \brief Concept for reading and writing characters. + + For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). + + For write-only stream, only need to implement Put() and Flush(). + +\code +concept Stream { + typename Ch; //!< Character type of the stream. + + //! Read the current character from stream without moving the read cursor. + Ch Peek() const; + + //! Read the current character from stream and moving the read cursor to next character. + Ch Take(); + + //! Get the current read cursor. + //! \return Number of characters read from start. + size_t Tell(); + + //! Begin writing operation at the current read pointer. + //! \return The begin writer pointer. + Ch* PutBegin(); + + //! Write a character. + void Put(Ch c); + + //! Flush the buffer. + void Flush(); + + //! End the writing operation. + //! \param begin The begin write pointer returned by PutBegin(). + //! \return Number of characters written. + size_t PutEnd(Ch* begin); +} +\endcode +*/ + +//! Provides additional information for stream. +/*! + By using traits pattern, this type provides a default configuration for stream. + For custom stream, this type can be specialized for other configuration. + See TEST(Reader, CustomStringStream) in readertest.cpp for example. +*/ +template +struct StreamTraits { + //! Whether to make local copy of stream for optimization during parsing. + /*! + By default, for safety, streams do not use local copy optimization. + Stream that can be copied fast should specialize this, like StreamTraits. + */ + enum { copyOptimization = 0 }; +}; + +//! Reserve n characters for writing to a stream. +template +inline void PutReserve(Stream& stream, size_t count) { + (void)stream; + (void)count; +} + +//! Write character to a stream, presuming buffer is reserved. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c) { + stream.Put(c); +} + +//! Put N copies of a character to a stream. +template +inline void PutN(Stream& stream, Ch c, size_t n) { + PutReserve(stream, n); + for (size_t i = 0; i < n; i++) + PutUnsafe(stream, c); +} + +/////////////////////////////////////////////////////////////////////////////// +// StringStream + +//! Read-only string stream. +/*! \note implements Stream concept +*/ +template +struct GenericStringStream { + typedef typename Encoding::Ch Ch; + + GenericStringStream(const Ch *src) : src_(src), head_(src) {} + + Ch Peek() const { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() const { return static_cast(src_ - head_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! String stream with UTF8 encoding. +typedef GenericStringStream > StringStream; + +/////////////////////////////////////////////////////////////////////////////// +// InsituStringStream + +//! A read-write string stream. +/*! This string stream is particularly designed for in-situ parsing. + \note implements Stream concept +*/ +template +struct GenericInsituStringStream { + typedef typename Encoding::Ch Ch; + + GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} + + // Read + Ch Peek() { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() { return static_cast(src_ - head_); } + + // Write + void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } + + Ch* PutBegin() { return dst_ = src_; } + size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } + void Flush() {} + + Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } + void Pop(size_t count) { dst_ -= count; } + + Ch* src_; + Ch* dst_; + Ch* head_; +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! Insitu string stream with UTF8 encoding. +typedef GenericInsituStringStream > InsituStringStream; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STREAM_H_ diff --git a/contrib/rapidjson/include/rapidjson/stringbuffer.h b/contrib/rapidjson/include/rapidjson/stringbuffer.h index 1c9c80b79..4e38b82c3 100644 --- a/contrib/rapidjson/include/rapidjson/stringbuffer.h +++ b/contrib/rapidjson/include/rapidjson/stringbuffer.h @@ -15,7 +15,8 @@ #ifndef RAPIDJSON_STRINGBUFFER_H_ #define RAPIDJSON_STRINGBUFFER_H_ -#include "rapidjson.h" +#include "stream.h" +#include "internal/stack.h" #if RAPIDJSON_HAS_CXX11_RVALUE_REFS #include // std::move @@ -23,6 +24,11 @@ #include "internal/stack.h" +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + RAPIDJSON_NAMESPACE_BEGIN //! Represents an in-memory output stream. @@ -48,6 +54,7 @@ public: #endif void Put(Ch c) { *stack_.template Push() = c; } + void PutUnsafe(Ch c) { *stack_.template PushUnsafe() = c; } void Flush() {} void Clear() { stack_.Clear(); } @@ -57,7 +64,10 @@ public: stack_.ShrinkToFit(); stack_.template Pop(1); } + + void Reserve(size_t count) { stack_.template Reserve(count); } Ch* Push(size_t count) { return stack_.template Push(count); } + Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe(count); } void Pop(size_t count) { stack_.template Pop(count); } const Ch* GetString() const { @@ -68,8 +78,12 @@ public: return stack_.template Bottom(); } + //! Get the size of string in bytes in the string buffer. size_t GetSize() const { return stack_.GetSize(); } + //! Get the length of string in Ch in the string buffer. + size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); } + static const size_t kDefaultCapacity = 256; mutable internal::Stack stack_; @@ -82,6 +96,16 @@ private: //! String buffer with UTF8 encoding typedef GenericStringBuffer > StringBuffer; +template +inline void PutReserve(GenericStringBuffer& stream, size_t count) { + stream.Reserve(count); +} + +template +inline void PutUnsafe(GenericStringBuffer& stream, typename Encoding::Ch c) { + stream.PutUnsafe(c); +} + //! Implement specialized version of PutN() with memset() for better performance. template<> inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { @@ -90,4 +114,8 @@ inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { RAPIDJSON_NAMESPACE_END +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + #endif // RAPIDJSON_STRINGBUFFER_H_ diff --git a/contrib/rapidjson/include/rapidjson/writer.h b/contrib/rapidjson/include/rapidjson/writer.h index e1eea38b9..e610ebb60 100644 --- a/contrib/rapidjson/include/rapidjson/writer.h +++ b/contrib/rapidjson/include/rapidjson/writer.h @@ -15,7 +15,8 @@ #ifndef RAPIDJSON_WRITER_H_ #define RAPIDJSON_WRITER_H_ -#include "rapidjson.h" +#include "stream.h" +#include "internal/meta.h" #include "internal/stack.h" #include "internal/strfunc.h" #include "internal/dtoa.h" @@ -23,8 +24,16 @@ #include "stringbuffer.h" #include // placement new -#if RAPIDJSON_HAS_STDSTRING -#include +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#elif defined(RAPIDJSON_NEON) +#include #endif #ifdef _MSC_VER @@ -32,8 +41,36 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant #endif +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + RAPIDJSON_NAMESPACE_BEGIN +/////////////////////////////////////////////////////////////////////////////// +// WriteFlag + +/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kWriteDefaultFlags definition. + + User can define this as any \c WriteFlag combinations. +*/ +#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS +#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags +#endif + +//! Combination of writeFlags +enum WriteFlag { + kWriteNoFlags = 0, //!< No flags are set. + kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings. + kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN. + kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS +}; + //! JSON writer /*! Writer implements the concept Handler. It generates JSON text by events to an output os. @@ -50,11 +87,13 @@ RAPIDJSON_NAMESPACE_BEGIN \tparam StackAllocator Type of allocator for allocating memory of stack. \note implements Handler concept */ -template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator> +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> class Writer { public: typedef typename SourceEncoding::Ch Ch; + static const int kDefaultMaxDecimalPlaces = 324; + //! Constructor /*! \param os Output stream. \param stackAllocator User supplied allocator. If it is null, it will create a private one. @@ -62,11 +101,18 @@ public: */ explicit Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : - os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), hasRoot_(false) {} + os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} explicit Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : - os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), hasRoot_(false) {} + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Writer(Writer&& rhs) : + os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) { + rhs.os_ = 0; + } +#endif //! Reset the writer with a new stream. /*! @@ -100,29 +146,66 @@ public: return hasRoot_ && level_stack_.Empty(); } + int GetMaxDecimalPlaces() const { + return maxDecimalPlaces_; + } + + //! Sets the maximum number of decimal places for double output. + /*! + This setting truncates the output with specified number of decimal places. + + For example, + + \code + writer.SetMaxDecimalPlaces(3); + writer.StartArray(); + writer.Double(0.12345); // "0.123" + writer.Double(0.0001); // "0.0" + writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent) + writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent) + writer.EndArray(); + \endcode + + The default setting does not truncate any decimal places. You can restore to this setting by calling + \code + writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces); + \endcode + */ + void SetMaxDecimalPlaces(int maxDecimalPlaces) { + maxDecimalPlaces_ = maxDecimalPlaces; + } + /*!@name Implementation of Handler \see Handler */ //@{ - bool Null() { Prefix(kNullType); return WriteNull(); } - bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return WriteBool(b); } - bool Int(int i) { Prefix(kNumberType); return WriteInt(i); } - bool Uint(unsigned u) { Prefix(kNumberType); return WriteUint(u); } - bool Int64(int64_t i64) { Prefix(kNumberType); return WriteInt64(i64); } - bool Uint64(uint64_t u64) { Prefix(kNumberType); return WriteUint64(u64); } + bool Null() { Prefix(kNullType); return EndValue(WriteNull()); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); } + bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); } + bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); } + bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(WriteUint64(u64)); } //! Writes the given \c double value to the stream /*! \param d The value to be written. \return Whether it is succeed. */ - bool Double(double d) { Prefix(kNumberType); return WriteDouble(d); } + bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + Prefix(kNumberType); + return EndValue(WriteString(str, length)); + } bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); (void)copy; Prefix(kStringType); - return WriteString(str, length); + return EndValue(WriteString(str, length)); } #if RAPIDJSON_HAS_STDSTRING @@ -138,16 +221,21 @@ public: } bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) + { + return Key(str.data(), SizeType(str.size())); + } +#endif bool EndObject(SizeType memberCount = 0) { (void)memberCount; - RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); - RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value level_stack_.template Pop(1); - bool ret = WriteEndObject(); - if (level_stack_.Empty()) // end of json text - os_->Flush(); - return ret; + return EndValue(WriteEndObject()); } bool StartArray() { @@ -161,10 +249,7 @@ public: RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); level_stack_.template Pop(1); - bool ret = WriteEndArray(); - if (level_stack_.Empty()) // end of json text - os_->Flush(); - return ret; + return EndValue(WriteEndArray()); } //@} @@ -172,11 +257,33 @@ public: //@{ //! Simpler but slower overload. - bool String(const Ch* str) { return String(str, internal::StrLen(str)); } - bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } - + bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); } + //@} + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + */ + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + Prefix(type); + return EndValue(WriteRawValue(json, length)); + } + + //! Flush the output stream. + /*! + Allows the user to flush the output stream immediately. + */ + void Flush() { + os_->Flush(); + } + protected: //! Information for each nested level struct Level { @@ -188,15 +295,18 @@ protected: static const size_t kDefaultLevelDepth = 32; bool WriteNull() { - os_->Put('n'); os_->Put('u'); os_->Put('l'); os_->Put('l'); return true; + PutReserve(*os_, 4); + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true; } bool WriteBool(bool b) { if (b) { - os_->Put('t'); os_->Put('r'); os_->Put('u'); os_->Put('e'); + PutReserve(*os_, 4); + PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e'); } else { - os_->Put('f'); os_->Put('a'); os_->Put('l'); os_->Put('s'); os_->Put('e'); + PutReserve(*os_, 5); + PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e'); } return true; } @@ -204,45 +314,69 @@ protected: bool WriteInt(int i) { char buffer[11]; const char* end = internal::i32toa(i, buffer); + PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) - os_->Put(*p); + PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteUint(unsigned u) { char buffer[10]; const char* end = internal::u32toa(u, buffer); + PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) - os_->Put(*p); + PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteInt64(int64_t i64) { char buffer[21]; const char* end = internal::i64toa(i64, buffer); + PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) - os_->Put(*p); + PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteUint64(uint64_t u64) { char buffer[20]; char* end = internal::u64toa(u64, buffer); + PutReserve(*os_, static_cast(end - buffer)); for (char* p = buffer; p != end; ++p) - os_->Put(*p); + PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + if (!(writeFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + char buffer[25]; - char* end = internal::dtoa(d, buffer); + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + PutReserve(*os_, static_cast(end - buffer)); for (char* p = buffer; p != end; ++p) - os_->Put(*p); + PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteString(const Ch* str, SizeType length) { - static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const typename OutputStream::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; static const char escape[256] = { #define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //0 1 2 3 4 5 6 7 8 9 A B C D E F @@ -255,22 +389,27 @@ protected: #undef Z16 }; - os_->Put('\"'); + if (TargetEncoding::supportUnicode) + PutReserve(*os_, 2 + length * 6); // "\uxxxx..." + else + PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..." + + PutUnsafe(*os_, '\"'); GenericStringStream is(str); - while (is.Tell() < length) { + while (ScanWriteUnescapedString(is, length)) { const Ch c = is.Peek(); - if (!TargetEncoding::supportUnicode && (unsigned)c >= 0x80) { + if (!TargetEncoding::supportUnicode && static_cast(c) >= 0x80) { // Unicode escaping unsigned codepoint; - if (!SourceEncoding::Decode(is, &codepoint)) + if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint))) return false; - os_->Put('\\'); - os_->Put('u'); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { - os_->Put(hexDigits[(codepoint >> 12) & 15]); - os_->Put(hexDigits[(codepoint >> 8) & 15]); - os_->Put(hexDigits[(codepoint >> 4) & 15]); - os_->Put(hexDigits[(codepoint ) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint ) & 15]); } else { RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); @@ -278,45 +417,59 @@ protected: unsigned s = codepoint - 0x010000; unsigned lead = (s >> 10) + 0xD800; unsigned trail = (s & 0x3FF) + 0xDC00; - os_->Put(hexDigits[(lead >> 12) & 15]); - os_->Put(hexDigits[(lead >> 8) & 15]); - os_->Put(hexDigits[(lead >> 4) & 15]); - os_->Put(hexDigits[(lead ) & 15]); - os_->Put('\\'); - os_->Put('u'); - os_->Put(hexDigits[(trail >> 12) & 15]); - os_->Put(hexDigits[(trail >> 8) & 15]); - os_->Put(hexDigits[(trail >> 4) & 15]); - os_->Put(hexDigits[(trail ) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(lead ) & 15]); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(trail ) & 15]); } } - else if ((sizeof(Ch) == 1 || (unsigned)c < 256) && escape[(unsigned char)c]) { + else if ((sizeof(Ch) == 1 || static_cast(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { is.Take(); - os_->Put('\\'); - os_->Put(escape[(unsigned char)c]); - if (escape[(unsigned char)c] == 'u') { - os_->Put('0'); - os_->Put('0'); - os_->Put(hexDigits[(unsigned char)c >> 4]); - os_->Put(hexDigits[(unsigned char)c & 0xF]); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, static_cast(escape[static_cast(c)])); + if (escape[static_cast(c)] == 'u') { + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, hexDigits[static_cast(c) >> 4]); + PutUnsafe(*os_, hexDigits[static_cast(c) & 0xF]); } } - else - if (!Transcoder::Transcode(is, *os_)) - return false; + else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; } - os_->Put('\"'); + PutUnsafe(*os_, '\"'); return true; } + bool ScanWriteUnescapedString(GenericStringStream& is, size_t length) { + return RAPIDJSON_LIKELY(is.Tell() < length); + } + bool WriteStartObject() { os_->Put('{'); return true; } bool WriteEndObject() { os_->Put('}'); return true; } bool WriteStartArray() { os_->Put('['); return true; } bool WriteEndArray() { os_->Put(']'); return true; } + bool WriteRawValue(const Ch* json, size_t length) { + PutReserve(*os_, length); + for (size_t i = 0; i < length; i++) { + RAPIDJSON_ASSERT(json[i] != '\0'); + PutUnsafe(*os_, json[i]); + } + return true; + } + void Prefix(Type type) { (void)type; - if (level_stack_.GetSize() != 0) { // this value is not at root + if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root Level* level = level_stack_.template Top(); if (level->valueCount > 0) { if (level->inArray) @@ -334,8 +487,16 @@ protected: } } + // Flush the value if it is the top level one. + bool EndValue(bool ret) { + if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text + Flush(); + return ret; + } + OutputStream* os_; internal::Stack level_stack_; + int maxDecimalPlaces_; bool hasRoot_; private: @@ -350,7 +511,7 @@ template<> inline bool Writer::WriteInt(int i) { char *buffer = os_->Push(11); const char* end = internal::i32toa(i, buffer); - os_->Pop(11 - (end - buffer)); + os_->Pop(static_cast(11 - (end - buffer))); return true; } @@ -358,7 +519,7 @@ template<> inline bool Writer::WriteUint(unsigned u) { char *buffer = os_->Push(10); const char* end = internal::u32toa(u, buffer); - os_->Pop(10 - (end - buffer)); + os_->Pop(static_cast(10 - (end - buffer))); return true; } @@ -366,7 +527,7 @@ template<> inline bool Writer::WriteInt64(int64_t i64) { char *buffer = os_->Push(21); const char* end = internal::i64toa(i64, buffer); - os_->Pop(21 - (end - buffer)); + os_->Pop(static_cast(21 - (end - buffer))); return true; } @@ -374,22 +535,177 @@ template<> inline bool Writer::WriteUint64(uint64_t u) { char *buffer = os_->Push(20); const char* end = internal::u64toa(u, buffer); - os_->Pop(20 - (end - buffer)); + os_->Pop(static_cast(20 - (end - buffer))); return true; } template<> inline bool Writer::WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag). + if (!(kWriteDefaultFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + char *buffer = os_->Push(25); - char* end = internal::dtoa(d, buffer); - os_->Pop(25 - (end - buffer)); + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + os_->Pop(static_cast(25 - (end - buffer))); return true; } +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (; p != endAligned; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType len; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + len = offset; +#else + len = static_cast(__builtin_ffs(r) - 1); +#endif + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#elif defined(RAPIDJSON_NEON) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (; p != endAligned; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + SizeType len = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + unsigned lz = (unsigned)__builtin_clzll(high); + len = 8 + (lz >> 3); + escaped = true; + } + } else { + unsigned lz = (unsigned)__builtin_clzll(low); + len = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + vst1q_u8(reinterpret_cast(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#endif // RAPIDJSON_NEON + RAPIDJSON_NAMESPACE_END #ifdef _MSC_VER RAPIDJSON_DIAG_POP #endif +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + #endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/contrib/rapidjson/license.txt b/contrib/rapidjson/license.txt index 879293afa..7ccc161c8 100644 --- a/contrib/rapidjson/license.txt +++ b/contrib/rapidjson/license.txt @@ -3,7 +3,7 @@ Tencent is pleased to support the open source community by making RapidJSON avai Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. If you have downloaded a copy of the RapidJSON binary from Tencent, please note that the RapidJSON binary is licensed under the MIT License. -If you have downloaded a copy of the RapidJSON source code from Tencent, please note that RapidJSON source code is licensed under the MIT License, except for the third-party components listed below which are subject to different license terms. Your integration of RapidJSON into your own projects may require compliance with the MIT License, as well as the other licenses applicable to the third-party components included within RapidJSON. +If you have downloaded a copy of the RapidJSON source code from Tencent, please note that RapidJSON source code is licensed under the MIT License, except for the third-party components listed below which are subject to different license terms. Your integration of RapidJSON into your own projects may require compliance with the MIT License, as well as the other licenses applicable to the third-party components included within RapidJSON. To avoid the problematic JSON license in your own projects, it's sufficient to exclude the bin/jsonchecker/ directory, as it's the only code under the JSON license. A copy of the MIT License is included in this file. Other dependencies and licenses: diff --git a/contrib/rapidjson/readme.md b/contrib/rapidjson/readme.md index 19da38667..b833a98e8 100644 --- a/contrib/rapidjson/readme.md +++ b/contrib/rapidjson/readme.md @@ -1,17 +1,17 @@ -![](doc/logo/rapidjson.png) +![RapidJSON logo](doc/logo/rapidjson.png) -![](https://img.shields.io/badge/release-v1.0.2-blue.png) +![Release version](https://img.shields.io/badge/release-v1.1.0-blue.svg) -## A fast JSON parser/generator for C++ with both SAX/DOM style API +## A fast JSON parser/generator for C++ with both SAX/DOM style API Tencent is pleased to support the open source community by making RapidJSON available. Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. -* [RapidJSON GitHub](https://github.com/miloyip/rapidjson/) +* [RapidJSON GitHub](https://github.com/Tencent/rapidjson/) * RapidJSON Documentation - * [English](http://miloyip.github.io/rapidjson/) - * [简体中文](http://miloyip.github.io/rapidjson/zh-cn/) + * [English](http://rapidjson.org/) + * [简体中文](http://rapidjson.org/zh-cn/) * [GitBook](https://www.gitbook.com/book/miloyip/rapidjson/) with downloadable PDF/EPUB/MOBI, without API reference. ## Build status @@ -20,33 +20,43 @@ Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights | :---------------: | :-----------------: | :-------------------: | | ![lin-badge] | ![win-badge] | ![cov-badge] | -[lin-badge]: https://travis-ci.org/miloyip/rapidjson.png?branch=master "Travis build status" -[lin-link]: https://travis-ci.org/miloyip/rapidjson "Travis build status" -[win-badge]: https://ci.appveyor.com/api/projects/status/u658dcuwxo14a8m9/branch/master "AppVeyor build status" -[win-link]: https://ci.appveyor.com/project/miloyip/rapidjson/branch/master "AppVeyor build status" -[cov-badge]: https://coveralls.io/repos/miloyip/rapidjson/badge.png?branch=master -[cov-link]: https://coveralls.io/r/miloyip/rapidjson?branch=master +[lin-badge]: https://travis-ci.org/Tencent/rapidjson.svg?branch=master "Travis build status" +[lin-link]: https://travis-ci.org/Tencent/rapidjson "Travis build status" +[win-badge]: https://ci.appveyor.com/api/projects/status/l6qulgqahcayidrf/branch/master?svg=true "AppVeyor build status" +[win-link]: https://ci.appveyor.com/project/miloyip/rapidjson-0fdqj/branch/master "AppVeyor build status" +[cov-badge]: https://coveralls.io/repos/Tencent/rapidjson/badge.svg?branch=master "Coveralls coverage" +[cov-link]: https://coveralls.io/r/Tencent/rapidjson?branch=master "Coveralls coverage" ## Introduction RapidJSON is a JSON parser and generator for C++. It was inspired by [RapidXml](http://rapidxml.sourceforge.net/). -* RapidJSON is small but complete. It supports both SAX and DOM style API. The SAX parser is only a half thousand lines of code. +* RapidJSON is **small** but **complete**. It supports both SAX and DOM style API. The SAX parser is only a half thousand lines of code. -* RapidJSON is fast. Its performance can be comparable to `strlen()`. It also optionally supports SSE2/SSE4.2 for acceleration. +* RapidJSON is **fast**. Its performance can be comparable to `strlen()`. It also optionally supports SSE2/SSE4.2 for acceleration. -* RapidJSON is self-contained. It does not depend on external libraries such as BOOST. It even does not depend on STL. +* RapidJSON is **self-contained** and **header-only**. It does not depend on external libraries such as BOOST. It even does not depend on STL. -* RapidJSON is memory friendly. Each JSON value occupies exactly 16/20 bytes for most 32/64-bit machines (excluding text string). By default it uses a fast memory allocator, and the parser allocates memory compactly during parsing. +* RapidJSON is **memory-friendly**. Each JSON value occupies exactly 16 bytes for most 32/64-bit machines (excluding text string). By default it uses a fast memory allocator, and the parser allocates memory compactly during parsing. -* RapidJSON is Unicode friendly. It supports UTF-8, UTF-16, UTF-32 (LE & BE), and their detection, validation and transcoding internally. For example, you can read a UTF-8 file and let RapidJSON transcode the JSON strings into UTF-16 in the DOM. It also supports surrogates and "\u0000" (null character). +* RapidJSON is **Unicode-friendly**. It supports UTF-8, UTF-16, UTF-32 (LE & BE), and their detection, validation and transcoding internally. For example, you can read a UTF-8 file and let RapidJSON transcode the JSON strings into UTF-16 in the DOM. It also supports surrogates and "\u0000" (null character). More features can be read [here](doc/features.md). -JSON(JavaScript Object Notation) is a light-weight data exchange format. RapidJSON should be in fully compliance with RFC7159/ECMA-404. More information about JSON can be obtained at +JSON(JavaScript Object Notation) is a light-weight data exchange format. RapidJSON should be in fully compliance with RFC7159/ECMA-404, with optional support of relaxed syntax. More information about JSON can be obtained at * [Introducing JSON](http://json.org/) -* [RFC7159: The JavaScript Object Notation (JSON) Data Interchange Format](http://www.ietf.org/rfc/rfc7159.txt) -* [Standard ECMA-404: The JSON Data Interchange Format](http://www.ecma-international.org/publications/standards/Ecma-404.htm) +* [RFC7159: The JavaScript Object Notation (JSON) Data Interchange Format](https://tools.ietf.org/html/rfc7159) +* [Standard ECMA-404: The JSON Data Interchange Format](https://www.ecma-international.org/publications/standards/Ecma-404.htm) + +## Highlights in v1.1 (2016-8-25) + +* Added [JSON Pointer](doc/pointer.md) +* Added [JSON Schema](doc/schema.md) +* Added [relaxed JSON syntax](doc/dom.md) (comment, trailing comma, NaN/Infinity) +* Iterating array/object with [C++11 Range-based for loop](doc/tutorial.md) +* Reduce memory overhead of each `Value` from 24 bytes to 16 bytes in x86-64 architecture. + +For other changes please refer to [change log](CHANGELOG.md). ## Compatibility @@ -63,9 +73,9 @@ Users can build and run the unit tests on their platform/compiler. RapidJSON is a header-only C++ library. Just copy the `include/rapidjson` folder to system or project's include path. RapidJSON uses following software as its dependencies: -* [CMake](http://www.cmake.org) as a general build tool -* (optional)[Doxygen](http://www.doxygen.org) to build documentation -* (optional)[googletest](https://code.google.com/p/googletest/) for unit and performance testing +* [CMake](https://cmake.org/) as a general build tool +* (optional) [Doxygen](http://www.doxygen.org) to build documentation +* (optional) [googletest](https://github.com/google/googletest) for unit and performance testing To generate user documentation and run tests please proceed with the steps below: @@ -74,7 +84,7 @@ To generate user documentation and run tests please proceed with the steps below 3. Change to `build` directory and run `cmake ..` command to configure your build. Windows users can do the same with cmake-gui application. 4. On Windows, build the solution found in the build directory. On Linux, run `make` from the build directory. -On successfull build you will find compiled test and example binaries in `bin` +On successful build you will find compiled test and example binaries in `bin` directory. The generated documentation will be available in `doc/html` directory of the build tree. To run tests after finished build please run `make test` or `ctest` from your build tree. You can get detailed output using `ctest @@ -126,4 +136,25 @@ The following diagram shows the process. ![simpledom](doc/diagram/simpledom.png) -More [examples](https://github.com/miloyip/rapidjson/tree/master/example) are available. +More [examples](https://github.com/Tencent/rapidjson/tree/master/example) are available: + +* DOM API + * [tutorial](https://github.com/Tencent/rapidjson/blob/master/example/tutorial/tutorial.cpp): Basic usage of DOM API. + +* SAX API + * [simplereader](https://github.com/Tencent/rapidjson/blob/master/example/simplereader/simplereader.cpp): Dumps all SAX events while parsing a JSON by `Reader`. + * [condense](https://github.com/Tencent/rapidjson/blob/master/example/condense/condense.cpp): A command line tool to rewrite a JSON, with all whitespaces removed. + * [pretty](https://github.com/Tencent/rapidjson/blob/master/example/pretty/pretty.cpp): A command line tool to rewrite a JSON with indents and newlines by `PrettyWriter`. + * [capitalize](https://github.com/Tencent/rapidjson/blob/master/example/capitalize/capitalize.cpp): A command line tool to capitalize strings in JSON. + * [messagereader](https://github.com/Tencent/rapidjson/blob/master/example/messagereader/messagereader.cpp): Parse a JSON message with SAX API. + * [serialize](https://github.com/Tencent/rapidjson/blob/master/example/serialize/serialize.cpp): Serialize a C++ object into JSON with SAX API. + * [jsonx](https://github.com/Tencent/rapidjson/blob/master/example/jsonx/jsonx.cpp): Implements a `JsonxWriter` which stringify SAX events into [JSONx](https://www-01.ibm.com/support/knowledgecenter/SS9H2Y_7.1.0/com.ibm.dp.doc/json_jsonx.html) (a kind of XML) format. The example is a command line tool which converts input JSON into JSONx format. + +* Schema + * [schemavalidator](https://github.com/Tencent/rapidjson/blob/master/example/schemavalidator/schemavalidator.cpp) : A command line tool to validate a JSON with a JSON schema. + +* Advanced + * [prettyauto](https://github.com/Tencent/rapidjson/blob/master/example/prettyauto/prettyauto.cpp): A modified version of [pretty](https://github.com/Tencent/rapidjson/blob/master/example/pretty/pretty.cpp) to automatically handle JSON with any UTF encodings. + * [parsebyparts](https://github.com/Tencent/rapidjson/blob/master/example/parsebyparts/parsebyparts.cpp): Implements an `AsyncDocumentParser` which can parse JSON in parts, using C++11 thread. + * [filterkey](https://github.com/Tencent/rapidjson/blob/master/example/filterkey/filterkey.cpp): A command line tool to remove all values with user-specified key. + * [filterkeydom](https://github.com/Tencent/rapidjson/blob/master/example/filterkeydom/filterkeydom.cpp): Same tool as above, but it demonstrates how to use a generator to populate a `Document`. From 9bcfce63dcb706b8aba3748fbd4321506a67a0a0 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 11:33:27 +0300 Subject: [PATCH 156/490] utMetadata: Fix memory leak --- test/unit/utMetadata.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/utMetadata.cpp b/test/unit/utMetadata.cpp index b44de1eb2..4109b068c 100644 --- a/test/unit/utMetadata.cpp +++ b/test/unit/utMetadata.cpp @@ -79,6 +79,7 @@ TEST_F( utMetadata, allocTest ) { EXPECT_EQ( 1U, data->mNumProperties ); EXPECT_NE( nullptr, data->mKeys ); EXPECT_NE( nullptr, data->mValues ); + aiMetadata::Dealloc( data ); } TEST_F( utMetadata, get_set_pod_Test ) { From 674fb5a46cf5b0202d13972acda9ef308f0c91f7 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 11:55:58 +0300 Subject: [PATCH 157/490] utRemoveVCProcess: Fix memory leak --- test/unit/utRemoveVCProcess.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/utRemoveVCProcess.cpp b/test/unit/utRemoveVCProcess.cpp index c78e80d3f..6caa72c11 100644 --- a/test/unit/utRemoveVCProcess.cpp +++ b/test/unit/utRemoveVCProcess.cpp @@ -70,7 +70,7 @@ TEST_F( utRevmoveVCProcess, issue1266_ProcessMeshTest_NoCrash ) { mesh->mNumVertices = 1; mesh->mColors[ 0 ] = new aiColor4D[ 2 ]; scene->mMeshes[ 0 ] = mesh; - RemoveVCProcess *process = new RemoveVCProcess; + std::unique_ptr process(new RemoveVCProcess); process->Execute( scene ); delete scene; } From 34acf47acda9067397dfa680ace52ae1bb4b7960 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 11:56:13 +0300 Subject: [PATCH 158/490] UnrealLoader: Fix IOStream leak --- code/UnrealLoader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/UnrealLoader.cpp b/code/UnrealLoader.cpp index c8382cb01..a79a2a5c5 100644 --- a/code/UnrealLoader.cpp +++ b/code/UnrealLoader.cpp @@ -157,7 +157,7 @@ void UnrealImporter::InternReadFile( const std::string& pFile, DefaultLogger::get()->debug("UNREAL: uc file is " + uc_path); // and open the files ... we can't live without them - IOStream* p = pIOHandler->Open(d_path); + std::unique_ptr p(pIOHandler->Open(d_path)); if (!p) throw DeadlyImportError("UNREAL: Unable to open _d file"); StreamReaderLE d_reader(pIOHandler->Open(d_path)); @@ -203,7 +203,7 @@ void UnrealImporter::InternReadFile( const std::string& pFile, d_reader.IncPtr(1); } - p = pIOHandler->Open(a_path); + p.reset(pIOHandler->Open(a_path)); if (!p) throw DeadlyImportError("UNREAL: Unable to open _a file"); StreamReaderLE a_reader(pIOHandler->Open(a_path)); From 316046f74826404783228bbae85edeaff927811d Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 12:09:26 +0300 Subject: [PATCH 159/490] AMFImporter: Fix memory leak --- code/AMFImporter_Postprocess.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/code/AMFImporter_Postprocess.cpp b/code/AMFImporter_Postprocess.cpp index 085336c51..789a11eb8 100644 --- a/code/AMFImporter_Postprocess.cpp +++ b/code/AMFImporter_Postprocess.cpp @@ -686,7 +686,6 @@ std::list mesh_idx; tmesh->mNumVertices = static_cast(vert_arr.size()); tmesh->mVertices = new aiVector3D[tmesh->mNumVertices]; tmesh->mColors[0] = new aiColor4D[tmesh->mNumVertices]; - tmesh->mFaces = new aiFace[face_list_cur.size()]; memcpy(tmesh->mVertices, vert_arr.data(), tmesh->mNumVertices * sizeof(aiVector3D)); memcpy(tmesh->mColors[0], col_arr.data(), tmesh->mNumVertices * sizeof(aiColor4D)); From b6d2b9179971d8950e4472fc0773f9c30b92da8d Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 12:35:17 +0300 Subject: [PATCH 160/490] OpenGEXImporter: Store ChildInfo in unique_ptr so they get automatically cleaned up --- code/OpenGEXImporter.cpp | 8 ++++---- code/OpenGEXImporter.h | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index d9c58e91c..0d2ed0520 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -1231,9 +1231,9 @@ void OpenGEXImporter::pushNode( aiNode *node, aiScene *pScene ) { if( m_nodeChildMap.end() == it ) { info = new ChildInfo; m_root = info; - m_nodeChildMap[ node->mParent ] = info; + m_nodeChildMap[ node->mParent ] = std::unique_ptr(info); } else { - info = it->second; + info = it->second.get(); } info->m_children.push_back( node ); } else { @@ -1243,9 +1243,9 @@ void OpenGEXImporter::pushNode( aiNode *node, aiScene *pScene ) { NodeChildMap::iterator it( m_nodeChildMap.find( node->mParent ) ); if( m_nodeChildMap.end() == it ) { info = new ChildInfo; - m_nodeChildMap[ node->mParent ] = info; + m_nodeChildMap[ node->mParent ] = std::unique_ptr(info); } else { - info = it->second; + info = it->second.get(); } info->m_children.push_back( node ); } diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index bb6b45140..948712869 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include namespace ODDLParser { class DDLNode; @@ -179,7 +180,7 @@ private: std::list m_children; }; ChildInfo *m_root; - typedef std::map NodeChildMap; + typedef std::map > NodeChildMap; NodeChildMap m_nodeChildMap; std::vector m_meshCache; From e7ff7b167f270e6eea910ccc18499acfc9a16665 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 12:38:44 +0300 Subject: [PATCH 161/490] OpenGEXImporter: Fix IOStream leak --- code/OpenGEXImporter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 0d2ed0520..24dabd1d4 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -307,6 +307,7 @@ void OpenGEXImporter::InternReadFile( const std::string &filename, aiScene *pSce std::vector buffer; TextFileToBuffer( file, buffer ); + pIOHandler->Close( file ); OpenDDLParser myParser; myParser.setBuffer( &buffer[ 0 ], buffer.size() ); From 568003a0d3f2f9dce1f7a8173a67d20c216559a4 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 12:43:49 +0300 Subject: [PATCH 162/490] OpenGEXImporter: Store RefInfo in unique_ptr so they get automatically cleaned up --- code/OpenGEXImporter.cpp | 8 ++++---- code/OpenGEXImporter.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 24dabd1d4..7073a5e76 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -525,7 +525,7 @@ void OpenGEXImporter::handleObjectRefNode( DDLNode *node, aiScene *pScene ) { m_currentNode->mNumMeshes = static_cast(objRefNames.size()); m_currentNode->mMeshes = new unsigned int[ objRefNames.size() ]; if ( !objRefNames.empty() ) { - m_unresolvedRefStack.push_back( new RefInfo( m_currentNode, RefInfo::MeshRef, objRefNames ) ); + m_unresolvedRefStack.push_back( std::unique_ptr( new RefInfo( m_currentNode, RefInfo::MeshRef, objRefNames ) ) ); } } else if ( m_tokenType == Grammar::LightNodeToken ) { // TODO! @@ -544,7 +544,7 @@ void OpenGEXImporter::handleMaterialRefNode( ODDLParser::DDLNode *node, aiScene std::vector matRefNames; getRefNames( node, matRefNames ); if( !matRefNames.empty() ) { - m_unresolvedRefStack.push_back( new RefInfo( m_currentNode, RefInfo::MaterialRef, matRefNames ) ); + m_unresolvedRefStack.push_back( std::unique_ptr( new RefInfo( m_currentNode, RefInfo::MaterialRef, matRefNames ) ) ); } } @@ -1165,8 +1165,8 @@ void OpenGEXImporter::resolveReferences() { } RefInfo *currentRefInfo( nullptr ); - for( std::vector::iterator it = m_unresolvedRefStack.begin(); it != m_unresolvedRefStack.end(); ++it ) { - currentRefInfo = *it; + for( auto it = m_unresolvedRefStack.begin(); it != m_unresolvedRefStack.end(); ++it ) { + currentRefInfo = it->get(); if( nullptr != currentRefInfo ) { aiNode *node( currentRefInfo->m_node ); if( RefInfo::MeshRef == currentRefInfo->m_type ) { diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index 948712869..aefd9ef1d 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -201,7 +201,7 @@ private: std::vector m_cameraCache; std::vector m_lightCache; std::vector m_nodeStack; - std::vector m_unresolvedRefStack; + std::vector > m_unresolvedRefStack; }; } // Namespace OpenGEX From 3f0bb9c63494c097cc3ddf6240af8c76023e7819 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 12:51:08 +0300 Subject: [PATCH 163/490] OpenGEXImporter: Copy materials to scene --- code/OpenGEXImporter.cpp | 14 ++++++++++++++ code/OpenGEXImporter.h | 1 + 2 files changed, 15 insertions(+) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 7073a5e76..47e3e3e8c 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -322,6 +322,7 @@ void OpenGEXImporter::InternReadFile( const std::string &filename, aiScene *pSce copyMeshes( pScene ); copyCameras( pScene ); copyLights( pScene ); + copyMaterials( pScene ); resolveReferences(); createNodeTree( pScene ); } @@ -1158,6 +1159,19 @@ void OpenGEXImporter::copyLights( aiScene *pScene ) { std::copy( m_lightCache.begin(), m_lightCache.end(), pScene->mLights ); } +//------------------------------------------------------------------------------------------------ +void OpenGEXImporter::copyMaterials( aiScene *pScene ) { + ai_assert( nullptr != pScene ); + + if ( m_materialCache.empty() ) { + return; + } + + pScene->mNumMaterials = static_cast(m_materialCache.size()); + pScene->mMaterials = new aiMaterial*[ pScene->mNumMaterials ]; + std::copy( m_materialCache.begin(), m_materialCache.end(), pScene->mMaterials ); +} + //------------------------------------------------------------------------------------------------ void OpenGEXImporter::resolveReferences() { if( m_unresolvedRefStack.empty() ) { diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index aefd9ef1d..c0cde579c 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -133,6 +133,7 @@ protected: void copyMeshes( aiScene *pScene ); void copyCameras( aiScene *pScene ); void copyLights( aiScene *pScene ); + void copyMaterials( aiScene *pScene ); void resolveReferences(); void pushNode( aiNode *node, aiScene *pScene ); aiNode *popNode(); From 341222697a02f41d6279d256b153a3b341c9e25e Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Thu, 5 Oct 2017 23:55:10 +1100 Subject: [PATCH 164/490] Address warnings on Clang 3.9 caused by having a static data member in a class template. --- code/BlenderBMesh.cpp | 6 +++++- code/BlenderLoader.cpp | 6 +++++- code/BlenderTessellator.cpp | 12 ++++++++++-- code/FBXImporter.cpp | 6 +++++- code/IFCLoader.cpp | 6 +++++- code/LogAux.h | 13 ++++++------- code/XGLLoader.cpp | 7 +++++-- 7 files changed, 41 insertions(+), 15 deletions(-) diff --git a/code/BlenderBMesh.cpp b/code/BlenderBMesh.cpp index c328947ae..8a13819a6 100644 --- a/code/BlenderBMesh.cpp +++ b/code/BlenderBMesh.cpp @@ -52,7 +52,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { - template< > const std::string LogFunctions< BlenderBMeshConverter >::log_prefix = "BLEND_BMESH: "; + template< > const char* LogFunctions< BlenderBMeshConverter >::Prefix() + { + static auto prefix = "BLEND_BMESH: "; + return prefix; + } } using namespace Assimp; diff --git a/code/BlenderLoader.cpp b/code/BlenderLoader.cpp index 1453ef000..6d4c97cbf 100644 --- a/code/BlenderLoader.cpp +++ b/code/BlenderLoader.cpp @@ -74,7 +74,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endif namespace Assimp { - template<> const std::string LogFunctions::log_prefix = "BLEND: "; + template<> const char* LogFunctions::Prefix() + { + static auto prefix = "BLEND: "; + return prefix; + } } using namespace Assimp; diff --git a/code/BlenderTessellator.cpp b/code/BlenderTessellator.cpp index 2eaa938dd..9f4c51048 100644 --- a/code/BlenderTessellator.cpp +++ b/code/BlenderTessellator.cpp @@ -59,7 +59,11 @@ static const unsigned int BLEND_TESS_MAGIC = 0x83ed9ac3; namspace Assimp { - template< > const std::string LogFunctions< BlenderTessellatorGL >::log_prefix = "BLEND_TESS_GL: "; + template< > const char* LogFunctions< BlenderTessellatorGL >::Prefix() + { + static auto prefix = "BLEND_TESS_GL: "; + return prefix; + } } using namespace Assimp; @@ -252,7 +256,11 @@ void BlenderTessellatorGL::TessellateError( GLenum errorCode, void* ) namespace Assimp { - template< > const std::string LogFunctions< BlenderTessellatorP2T >::log_prefix = "BLEND_TESS_P2T: "; + template< > const char* LogFunctions< BlenderTessellatorP2T >::Prefix() + { + static auto prefix = "BLEND_TESS_P2T: "; + return prefix; + } } using namespace Assimp; diff --git a/code/FBXImporter.cpp b/code/FBXImporter.cpp index 0ebf13e7d..51e41b8f4 100644 --- a/code/FBXImporter.cpp +++ b/code/FBXImporter.cpp @@ -59,7 +59,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include namespace Assimp { - template<> const std::string LogFunctions::log_prefix = "FBX: "; + template<> const char* LogFunctions::Prefix() + { + static auto prefix = "FBX: "; + return prefix; + } } using namespace Assimp; diff --git a/code/IFCLoader.cpp b/code/IFCLoader.cpp index e8632fa9d..85382d467 100644 --- a/code/IFCLoader.cpp +++ b/code/IFCLoader.cpp @@ -66,7 +66,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { - template<> const std::string LogFunctions::log_prefix = "IFC: "; + template<> const char* LogFunctions::Prefix() + { + static auto prefix = "IFC: "; + return prefix; + } } using namespace Assimp; diff --git a/code/LogAux.h b/code/LogAux.h index 432da5cd5..08b1b3c17 100644 --- a/code/LogAux.h +++ b/code/LogAux.h @@ -60,34 +60,34 @@ public: // ------------------------------------------------------------------------------------------------ static void ThrowException(const std::string& msg) { - throw DeadlyImportError(log_prefix+msg); + throw DeadlyImportError(Prefix()+msg); } // ------------------------------------------------------------------------------------------------ static void LogWarn(const Formatter::format& message) { if (!DefaultLogger::isNullLogger()) { - DefaultLogger::get()->warn(log_prefix+(std::string)message); + DefaultLogger::get()->warn(Prefix() +(std::string)message); } } // ------------------------------------------------------------------------------------------------ static void LogError(const Formatter::format& message) { if (!DefaultLogger::isNullLogger()) { - DefaultLogger::get()->error(log_prefix+(std::string)message); + DefaultLogger::get()->error(Prefix() +(std::string)message); } } // ------------------------------------------------------------------------------------------------ static void LogInfo(const Formatter::format& message) { if (!DefaultLogger::isNullLogger()) { - DefaultLogger::get()->info(log_prefix+(std::string)message); + DefaultLogger::get()->info(Prefix() +(std::string)message); } } // ------------------------------------------------------------------------------------------------ static void LogDebug(const Formatter::format& message) { if (!DefaultLogger::isNullLogger()) { - DefaultLogger::get()->debug(log_prefix+(std::string)message); + DefaultLogger::get()->debug(Prefix() +(std::string)message); } } @@ -125,8 +125,7 @@ public: #endif private: - - static const std::string log_prefix; + static const char* Prefix(); }; diff --git a/code/XGLLoader.cpp b/code/XGLLoader.cpp index bafcda3f2..df33229a6 100644 --- a/code/XGLLoader.cpp +++ b/code/XGLLoader.cpp @@ -83,8 +83,11 @@ struct free_it }; namespace Assimp { // this has to be in here because LogFunctions is in ::Assimp -template<> const std::string LogFunctions::log_prefix = "XGL: "; - + template<> const char* LogFunctions::Prefix() + { + static auto prefix = "XGL: "; + return prefix; + } } static const aiImporterDesc desc = { From 3803a5181cc1e0e42de26854244db988829b4036 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Fri, 6 Oct 2017 20:32:33 +1100 Subject: [PATCH 165/490] Fixed warnings on MSVC caused by implicit conversions from double to float. --- code/glTF2Asset.inl | 4 ++-- code/glTF2Exporter.cpp | 6 +++--- code/glTFExporter.cpp | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 3082ebfab..8b50fa1d3 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -724,7 +724,7 @@ namespace { SetTextureProperties(r, prop, out); if (Value* scale = FindNumber(*prop, "scale")) { - out.scale = scale->GetDouble(); + out.scale = static_cast(scale->GetDouble()); } } } @@ -735,7 +735,7 @@ namespace { SetTextureProperties(r, prop, out); if (Value* strength = FindNumber(*prop, "strength")) { - out.strength = strength->GetDouble(); + out.strength = static_cast(strength->GetDouble()); } } } diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 4ed8f20ff..8f46f7dc7 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -563,7 +563,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref& meshRef, Ref(jointNamesIndex); vertexWeightData[vertexId][jointsPerVertex[vertexId]] = vertWeight; jointsPerVertex[vertexId] += 1; @@ -872,7 +872,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, RefmNumPositionKeys / numKeyframes; // mTime is measured in ticks, but GLTF time is measured in seconds, so convert. // Check if we have to cast type here. e.g. uint16_t() - timeData[i] = nodeChannel->mPositionKeys[frameIndex].mTime / ticksPerSecond; + timeData[i] = static_cast(nodeChannel->mPositionKeys[frameIndex].mTime / ticksPerSecond); } Ref timeAccessor = ExportData(mAsset, animId, buffer, static_cast(numKeyframes), &timeData[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_FLOAT); @@ -953,7 +953,7 @@ void glTF2Exporter::ExportAnimations() Ref animRef = mAsset->animations.Create(name); // Parameters - ExtractAnimationData(*mAsset, name, animRef, bufferRef, nodeChannel, anim->mTicksPerSecond); + ExtractAnimationData(*mAsset, name, animRef, bufferRef, nodeChannel, static_cast(anim->mTicksPerSecond)); for (unsigned int j = 0; j < 3; ++j) { std::string channelType; diff --git a/code/glTFExporter.cpp b/code/glTFExporter.cpp index a5df09ac7..92abcc15c 100644 --- a/code/glTFExporter.cpp +++ b/code/glTFExporter.cpp @@ -473,7 +473,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref& meshRef, Ref(jointNamesIndex); vertexWeightData[vertexId][jointsPerVertex[vertexId]] = vertWeight; jointsPerVertex[vertexId] += 1; @@ -872,7 +872,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, RefmNumPositionKeys / numKeyframes; // mTime is measured in ticks, but GLTF time is measured in seconds, so convert. // Check if we have to cast type here. e.g. uint16_t() - timeData[i] = nodeChannel->mPositionKeys[frameIndex].mTime / ticksPerSecond; + timeData[i] = static_cast(nodeChannel->mPositionKeys[frameIndex].mTime / ticksPerSecond); } Ref timeAccessor = ExportData(mAsset, animId, buffer, static_cast(numKeyframes), &timeData[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_FLOAT); @@ -953,7 +953,7 @@ void glTFExporter::ExportAnimations() Ref animRef = mAsset->animations.Create(name); /******************* Parameters ********************/ - ExtractAnimationData(*mAsset, name, animRef, bufferRef, nodeChannel, anim->mTicksPerSecond); + ExtractAnimationData(*mAsset, name, animRef, bufferRef, nodeChannel, static_cast(anim->mTicksPerSecond)); for (unsigned int j = 0; j < 3; ++j) { std::string channelType; From c1f93a69aeea260c729653b1f50607e825f96cc6 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 6 Oct 2017 15:42:05 +0200 Subject: [PATCH 166/490] Revert "WIP: Enable Travis clang build" --- .travis.sh | 2 +- .travis.yml | 14 - CMakeLists.txt | 13 + code/AMFImporter_Postprocess.cpp | 1 + code/FBXImporter.h | 8 - code/IFCLoader.h | 7 - code/OpenGEXImporter.cpp | 31 +- code/OpenGEXImporter.h | 6 +- code/UnrealLoader.cpp | 4 +- code/glTF2Asset.inl | 4 +- code/glTF2Exporter.cpp | 6 +- code/glTFExporter.cpp | 6 +- .../rapidjson/include/rapidjson/allocators.h | 42 +- .../rapidjson/include/rapidjson/document.h | 1073 ++------- .../include/rapidjson/encodedstream.h | 58 +- .../rapidjson/include/rapidjson/encodings.h | 233 +- .../rapidjson/include/rapidjson/error/en.h | 21 +- .../rapidjson/include/rapidjson/error/error.h | 27 +- .../include/rapidjson/filereadstream.h | 13 +- .../include/rapidjson/filewritestream.h | 17 +- contrib/rapidjson/include/rapidjson/fwd.h | 151 -- .../include/rapidjson/internal/biginteger.h | 14 +- .../include/rapidjson/internal/diyfp.h | 19 +- .../include/rapidjson/internal/dtoa.h | 50 +- .../include/rapidjson/internal/ieee754.h | 3 +- .../include/rapidjson/internal/regex.h | 734 ------ .../include/rapidjson/internal/stack.h | 70 +- .../include/rapidjson/internal/strfunc.h | 32 +- .../include/rapidjson/internal/strtod.h | 39 +- .../include/rapidjson/internal/swap.h | 9 - .../include/rapidjson/istreamwrapper.h | 115 - .../include/rapidjson/memorybuffer.h | 2 +- .../include/rapidjson/memorystream.h | 16 +- .../include/rapidjson/msinttypes/stdint.h | 8 +- .../include/rapidjson/ostreamwrapper.h | 81 - contrib/rapidjson/include/rapidjson/pointer.h | 131 +- .../include/rapidjson/prettywriter.h | 98 +- .../rapidjson/include/rapidjson/rapidjson.h | 314 +-- contrib/rapidjson/include/rapidjson/reader.h | 1345 +++-------- contrib/rapidjson/include/rapidjson/schema.h | 2016 ----------------- contrib/rapidjson/include/rapidjson/stream.h | 179 -- .../include/rapidjson/stringbuffer.h | 30 +- contrib/rapidjson/include/rapidjson/writer.h | 468 +--- contrib/rapidjson/license.txt | 2 +- contrib/rapidjson/readme.md | 81 +- test/unit/TestModelFactory.h | 2 +- test/unit/utMetadata.cpp | 1 - test/unit/utRemoveVCProcess.cpp | 2 +- 48 files changed, 1060 insertions(+), 6538 deletions(-) delete mode 100644 contrib/rapidjson/include/rapidjson/fwd.h delete mode 100644 contrib/rapidjson/include/rapidjson/internal/regex.h delete mode 100644 contrib/rapidjson/include/rapidjson/istreamwrapper.h delete mode 100644 contrib/rapidjson/include/rapidjson/ostreamwrapper.h delete mode 100644 contrib/rapidjson/include/rapidjson/schema.h delete mode 100644 contrib/rapidjson/include/rapidjson/stream.h diff --git a/.travis.sh b/.travis.sh index 1ab1ee2b1..f19c3a000 100755 --- a/.travis.sh +++ b/.travis.sh @@ -1,6 +1,6 @@ function generate() { - cmake -G "Unix Makefiles" -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -DBUILD_SHARED_LIBS=$SHARED_BUILD -DASSIMP_COVERALLS=$ENABLE_COVERALLS -DASSIMP_WERROR=ON -DASSIMP_ASAN=$ASAN + cmake -G "Unix Makefiles" -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -DBUILD_SHARED_LIBS=$SHARED_BUILD -DASSIMP_COVERALLS=$ENABLE_COVERALLS -DASSIMP_ERROR=ON -DASSIMP_ASAN=$ASAN } if [ $ANDROID ]; then diff --git a/.travis.yml b/.travis.yml index 2eab656cc..d59689f78 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,10 +25,6 @@ branches: os: - linux -compiler: - - gcc - - clang - env: global: # COVERITY_SCAN_TOKEN @@ -38,10 +34,6 @@ env: matrix: exclude: - os: linux - compiler: gcc - env: - - os: linux - compiler: clang env: include: @@ -57,12 +49,6 @@ matrix: - os: linux compiler: gcc env: LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF - - os: linux - compiler: clang - env: LINUX=1 TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=ON - - os: linux - compiler: clang - env: LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF install: - if [ $ANDROID ]; then wget -c http://dl.google.com/android/ndk/android-ndk-${PV}-${PLATF}.tar.bz2 && tar xf android-ndk-${PV}-${PLATF}.tar.bz2 ; fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 0aae76837..5c0fe062e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,6 +151,19 @@ IF(ASSIMP_DOUBLE_PRECISION) ADD_DEFINITIONS(-DASSIMP_DOUBLE_PRECISION) ENDIF(ASSIMP_DOUBLE_PRECISION) +# Check for OpenMP support +find_package(OpenMP) +if (OPENMP_FOUND) + SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") + SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") + + IF(MSVC) + IF(MSVC_VERSION GREATER 1910) + SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:twoPhase-") + ENDIF() + ENDIF() +endif() + CONFIGURE_FILE( ${CMAKE_CURRENT_LIST_DIR}/revision.h.in ${CMAKE_CURRENT_BINARY_DIR}/revision.h diff --git a/code/AMFImporter_Postprocess.cpp b/code/AMFImporter_Postprocess.cpp index 789a11eb8..085336c51 100644 --- a/code/AMFImporter_Postprocess.cpp +++ b/code/AMFImporter_Postprocess.cpp @@ -686,6 +686,7 @@ std::list mesh_idx; tmesh->mNumVertices = static_cast(vert_arr.size()); tmesh->mVertices = new aiVector3D[tmesh->mNumVertices]; tmesh->mColors[0] = new aiColor4D[tmesh->mNumVertices]; + tmesh->mFaces = new aiFace[face_list_cur.size()]; memcpy(tmesh->mVertices, vert_arr.data(), tmesh->mNumVertices * sizeof(aiVector3D)); memcpy(tmesh->mColors[0], col_arr.data(), tmesh->mNumVertices * sizeof(aiColor4D)); diff --git a/code/FBXImporter.h b/code/FBXImporter.h index 54be8384e..43be97ffa 100644 --- a/code/FBXImporter.h +++ b/code/FBXImporter.h @@ -58,14 +58,6 @@ namespace Formatter { typedef class basic_formatter< char, std::char_traits, std::allocator > format; } -#ifndef _MSC_VER -// GCC and Clang need to see this explicit declaration to avoid warning -// MSVC complains about redeclaration even though this is just the -// declaration, not the definition -class FBXImporter; -template<> const std::string LogFunctions::log_prefix; -#endif // _MSC_VER - // ------------------------------------------------------------------------------------------- /** Load the Autodesk FBX file format. diff --git a/code/IFCLoader.h b/code/IFCLoader.h index 000683bb8..4cf116f8e 100644 --- a/code/IFCLoader.h +++ b/code/IFCLoader.h @@ -60,13 +60,6 @@ namespace Assimp { class DB; } -#ifndef _MSC_VER - // GCC and Clang need to see this explicit declaration to avoid warning - // MSVC complains about redeclaration even though this is just the - // declaration, not the definition - class IFCImporter; - template<> const std::string LogFunctions::log_prefix; -#endif // _MSC_VER // ------------------------------------------------------------------------------------------- /** Load the IFC format, which is an open specification to describe building and construction diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 47e3e3e8c..d9c58e91c 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -307,7 +307,6 @@ void OpenGEXImporter::InternReadFile( const std::string &filename, aiScene *pSce std::vector buffer; TextFileToBuffer( file, buffer ); - pIOHandler->Close( file ); OpenDDLParser myParser; myParser.setBuffer( &buffer[ 0 ], buffer.size() ); @@ -322,7 +321,6 @@ void OpenGEXImporter::InternReadFile( const std::string &filename, aiScene *pSce copyMeshes( pScene ); copyCameras( pScene ); copyLights( pScene ); - copyMaterials( pScene ); resolveReferences(); createNodeTree( pScene ); } @@ -526,7 +524,7 @@ void OpenGEXImporter::handleObjectRefNode( DDLNode *node, aiScene *pScene ) { m_currentNode->mNumMeshes = static_cast(objRefNames.size()); m_currentNode->mMeshes = new unsigned int[ objRefNames.size() ]; if ( !objRefNames.empty() ) { - m_unresolvedRefStack.push_back( std::unique_ptr( new RefInfo( m_currentNode, RefInfo::MeshRef, objRefNames ) ) ); + m_unresolvedRefStack.push_back( new RefInfo( m_currentNode, RefInfo::MeshRef, objRefNames ) ); } } else if ( m_tokenType == Grammar::LightNodeToken ) { // TODO! @@ -545,7 +543,7 @@ void OpenGEXImporter::handleMaterialRefNode( ODDLParser::DDLNode *node, aiScene std::vector matRefNames; getRefNames( node, matRefNames ); if( !matRefNames.empty() ) { - m_unresolvedRefStack.push_back( std::unique_ptr( new RefInfo( m_currentNode, RefInfo::MaterialRef, matRefNames ) ) ); + m_unresolvedRefStack.push_back( new RefInfo( m_currentNode, RefInfo::MaterialRef, matRefNames ) ); } } @@ -1159,19 +1157,6 @@ void OpenGEXImporter::copyLights( aiScene *pScene ) { std::copy( m_lightCache.begin(), m_lightCache.end(), pScene->mLights ); } -//------------------------------------------------------------------------------------------------ -void OpenGEXImporter::copyMaterials( aiScene *pScene ) { - ai_assert( nullptr != pScene ); - - if ( m_materialCache.empty() ) { - return; - } - - pScene->mNumMaterials = static_cast(m_materialCache.size()); - pScene->mMaterials = new aiMaterial*[ pScene->mNumMaterials ]; - std::copy( m_materialCache.begin(), m_materialCache.end(), pScene->mMaterials ); -} - //------------------------------------------------------------------------------------------------ void OpenGEXImporter::resolveReferences() { if( m_unresolvedRefStack.empty() ) { @@ -1179,8 +1164,8 @@ void OpenGEXImporter::resolveReferences() { } RefInfo *currentRefInfo( nullptr ); - for( auto it = m_unresolvedRefStack.begin(); it != m_unresolvedRefStack.end(); ++it ) { - currentRefInfo = it->get(); + for( std::vector::iterator it = m_unresolvedRefStack.begin(); it != m_unresolvedRefStack.end(); ++it ) { + currentRefInfo = *it; if( nullptr != currentRefInfo ) { aiNode *node( currentRefInfo->m_node ); if( RefInfo::MeshRef == currentRefInfo->m_type ) { @@ -1246,9 +1231,9 @@ void OpenGEXImporter::pushNode( aiNode *node, aiScene *pScene ) { if( m_nodeChildMap.end() == it ) { info = new ChildInfo; m_root = info; - m_nodeChildMap[ node->mParent ] = std::unique_ptr(info); + m_nodeChildMap[ node->mParent ] = info; } else { - info = it->second.get(); + info = it->second; } info->m_children.push_back( node ); } else { @@ -1258,9 +1243,9 @@ void OpenGEXImporter::pushNode( aiNode *node, aiScene *pScene ) { NodeChildMap::iterator it( m_nodeChildMap.find( node->mParent ) ); if( m_nodeChildMap.end() == it ) { info = new ChildInfo; - m_nodeChildMap[ node->mParent ] = std::unique_ptr(info); + m_nodeChildMap[ node->mParent ] = info; } else { - info = it->second.get(); + info = it->second; } info->m_children.push_back( node ); } diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index c0cde579c..bb6b45140 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -49,7 +49,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include namespace ODDLParser { class DDLNode; @@ -133,7 +132,6 @@ protected: void copyMeshes( aiScene *pScene ); void copyCameras( aiScene *pScene ); void copyLights( aiScene *pScene ); - void copyMaterials( aiScene *pScene ); void resolveReferences(); void pushNode( aiNode *node, aiScene *pScene ); aiNode *popNode(); @@ -181,7 +179,7 @@ private: std::list m_children; }; ChildInfo *m_root; - typedef std::map > NodeChildMap; + typedef std::map NodeChildMap; NodeChildMap m_nodeChildMap; std::vector m_meshCache; @@ -202,7 +200,7 @@ private: std::vector m_cameraCache; std::vector m_lightCache; std::vector m_nodeStack; - std::vector > m_unresolvedRefStack; + std::vector m_unresolvedRefStack; }; } // Namespace OpenGEX diff --git a/code/UnrealLoader.cpp b/code/UnrealLoader.cpp index a79a2a5c5..c8382cb01 100644 --- a/code/UnrealLoader.cpp +++ b/code/UnrealLoader.cpp @@ -157,7 +157,7 @@ void UnrealImporter::InternReadFile( const std::string& pFile, DefaultLogger::get()->debug("UNREAL: uc file is " + uc_path); // and open the files ... we can't live without them - std::unique_ptr p(pIOHandler->Open(d_path)); + IOStream* p = pIOHandler->Open(d_path); if (!p) throw DeadlyImportError("UNREAL: Unable to open _d file"); StreamReaderLE d_reader(pIOHandler->Open(d_path)); @@ -203,7 +203,7 @@ void UnrealImporter::InternReadFile( const std::string& pFile, d_reader.IncPtr(1); } - p.reset(pIOHandler->Open(a_path)); + p = pIOHandler->Open(a_path); if (!p) throw DeadlyImportError("UNREAL: Unable to open _a file"); StreamReaderLE a_reader(pIOHandler->Open(a_path)); diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 8b50fa1d3..3082ebfab 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -724,7 +724,7 @@ namespace { SetTextureProperties(r, prop, out); if (Value* scale = FindNumber(*prop, "scale")) { - out.scale = static_cast(scale->GetDouble()); + out.scale = scale->GetDouble(); } } } @@ -735,7 +735,7 @@ namespace { SetTextureProperties(r, prop, out); if (Value* strength = FindNumber(*prop, "strength")) { - out.strength = static_cast(strength->GetDouble()); + out.strength = strength->GetDouble(); } } } diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 8f46f7dc7..4ed8f20ff 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -563,7 +563,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref& meshRef, Ref(jointNamesIndex); + vertexJointData[vertexId][jointsPerVertex[vertexId]] = jointNamesIndex; vertexWeightData[vertexId][jointsPerVertex[vertexId]] = vertWeight; jointsPerVertex[vertexId] += 1; @@ -872,7 +872,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, RefmNumPositionKeys / numKeyframes; // mTime is measured in ticks, but GLTF time is measured in seconds, so convert. // Check if we have to cast type here. e.g. uint16_t() - timeData[i] = static_cast(nodeChannel->mPositionKeys[frameIndex].mTime / ticksPerSecond); + timeData[i] = nodeChannel->mPositionKeys[frameIndex].mTime / ticksPerSecond; } Ref timeAccessor = ExportData(mAsset, animId, buffer, static_cast(numKeyframes), &timeData[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_FLOAT); @@ -953,7 +953,7 @@ void glTF2Exporter::ExportAnimations() Ref animRef = mAsset->animations.Create(name); // Parameters - ExtractAnimationData(*mAsset, name, animRef, bufferRef, nodeChannel, static_cast(anim->mTicksPerSecond)); + ExtractAnimationData(*mAsset, name, animRef, bufferRef, nodeChannel, anim->mTicksPerSecond); for (unsigned int j = 0; j < 3; ++j) { std::string channelType; diff --git a/code/glTFExporter.cpp b/code/glTFExporter.cpp index 92abcc15c..a5df09ac7 100644 --- a/code/glTFExporter.cpp +++ b/code/glTFExporter.cpp @@ -473,7 +473,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref& meshRef, Ref(jointNamesIndex); + vertexJointData[vertexId][jointsPerVertex[vertexId]] = jointNamesIndex; vertexWeightData[vertexId][jointsPerVertex[vertexId]] = vertWeight; jointsPerVertex[vertexId] += 1; @@ -872,7 +872,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, RefmNumPositionKeys / numKeyframes; // mTime is measured in ticks, but GLTF time is measured in seconds, so convert. // Check if we have to cast type here. e.g. uint16_t() - timeData[i] = static_cast(nodeChannel->mPositionKeys[frameIndex].mTime / ticksPerSecond); + timeData[i] = nodeChannel->mPositionKeys[frameIndex].mTime / ticksPerSecond; } Ref timeAccessor = ExportData(mAsset, animId, buffer, static_cast(numKeyframes), &timeData[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_FLOAT); @@ -953,7 +953,7 @@ void glTFExporter::ExportAnimations() Ref animRef = mAsset->animations.Create(name); /******************* Parameters ********************/ - ExtractAnimationData(*mAsset, name, animRef, bufferRef, nodeChannel, static_cast(anim->mTicksPerSecond)); + ExtractAnimationData(*mAsset, name, animRef, bufferRef, nodeChannel, anim->mTicksPerSecond); for (unsigned int j = 0; j < 3; ++j) { std::string channelType; diff --git a/contrib/rapidjson/include/rapidjson/allocators.h b/contrib/rapidjson/include/rapidjson/allocators.h index 655f4a385..d74a67155 100644 --- a/contrib/rapidjson/include/rapidjson/allocators.h +++ b/contrib/rapidjson/include/rapidjson/allocators.h @@ -179,10 +179,9 @@ public: size = RAPIDJSON_ALIGN(size); if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) - if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) - return NULL; + AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size); - void *buffer = reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size; + void *buffer = reinterpret_cast(chunkHead_ + 1) + chunkHead_->size; chunkHead_->size += size; return buffer; } @@ -195,16 +194,14 @@ public: if (newSize == 0) return NULL; - originalSize = RAPIDJSON_ALIGN(originalSize); - newSize = RAPIDJSON_ALIGN(newSize); - // Do not shrink if new size is smaller than original if (originalSize >= newSize) return originalPtr; // Simply expand it if it is the last allocation and there is sufficient space - if (originalPtr == reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) { + if (originalPtr == (char *)(chunkHead_ + 1) + chunkHead_->size - originalSize) { size_t increment = static_cast(newSize - originalSize); + increment = RAPIDJSON_ALIGN(increment); if (chunkHead_->size + increment <= chunkHead_->capacity) { chunkHead_->size += increment; return originalPtr; @@ -212,13 +209,11 @@ public: } // Realloc process: allocate and copy memory, do not free original buffer. - if (void* newBuffer = Malloc(newSize)) { - if (originalSize) - std::memcpy(newBuffer, originalPtr, originalSize); - return newBuffer; - } - else - return NULL; + void* newBuffer = Malloc(newSize); + RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly. + if (originalSize) + std::memcpy(newBuffer, originalPtr, originalSize); + return newBuffer; } //! Frees a memory block (concept Allocator) @@ -232,20 +227,15 @@ private: //! Creates a new chunk. /*! \param capacity Capacity of the chunk in bytes. - \return true if success. */ - bool AddChunk(size_t capacity) { + void AddChunk(size_t capacity) { if (!baseAllocator_) - ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)(); - if (ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) { - chunk->capacity = capacity; - chunk->size = 0; - chunk->next = chunkHead_; - chunkHead_ = chunk; - return true; - } - else - return false; + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator()); + ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(sizeof(ChunkHeader) + capacity)); + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = chunkHead_; + chunkHead_ = chunk; } static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity. diff --git a/contrib/rapidjson/include/rapidjson/document.h b/contrib/rapidjson/include/rapidjson/document.h index 93b091f64..c6acbd907 100644 --- a/contrib/rapidjson/include/rapidjson/document.h +++ b/contrib/rapidjson/include/rapidjson/document.h @@ -20,29 +20,40 @@ #include "reader.h" #include "internal/meta.h" #include "internal/strfunc.h" -#include "memorystream.h" -#include "encodedstream.h" #include // placement new -#include -RAPIDJSON_DIAG_PUSH #ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant -RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data -#endif - -#ifdef __clang__ -RAPIDJSON_DIAG_OFF(padded) -RAPIDJSON_DIAG_OFF(switch-enum) -RAPIDJSON_DIAG_OFF(c++98-compat) -#endif - -#ifdef __GNUC__ +#elif defined(__GNUC__) +RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) -#if __GNUC__ >= 6 -RAPIDJSON_DIAG_OFF(terminate) // ignore throwing RAPIDJSON_ASSERT in RAPIDJSON_NOEXCEPT functions #endif -#endif // __GNUC__ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_HAS_STDSTRING + +#ifndef RAPIDJSON_HAS_STDSTRING +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation +#else +#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default +#endif +/*! \def RAPIDJSON_HAS_STDSTRING + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for \c std::string + + By defining this preprocessor symbol to \c 1, several convenience functions for using + \ref rapidjson::GenericValue with \c std::string are enabled, especially + for construction and comparison. + + \hideinitializer +*/ +#endif // !defined(RAPIDJSON_HAS_STDSTRING) + +#if RAPIDJSON_HAS_STDSTRING +#include +#endif // RAPIDJSON_HAS_STDSTRING #ifndef RAPIDJSON_NOMEMBERITERATORCLASS #include // std::iterator, std::random_access_iterator_tag @@ -58,9 +69,6 @@ RAPIDJSON_NAMESPACE_BEGIN template class GenericValue; -template -class GenericDocument; - //! Name-value pair in a JSON object value. /*! This class was internal to GenericValue. It used to be a inner struct. @@ -147,7 +155,6 @@ public: Otherwise, the copy constructor is implicitly defined. */ GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} - Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; } //! @name stepping //@{ @@ -250,7 +257,6 @@ struct GenericStringRef { typedef CharType Ch; //!< character type of the string //! Create string reference from \c const character array -#ifndef __clang__ // -Wdocumentation /*! This constructor implicitly creates a constant string reference from a \c const character array. It has better performance than @@ -273,13 +279,11 @@ struct GenericStringRef { In such cases, the referenced string should be \b copied to the GenericValue instead. */ -#endif template GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT : s(str), length(N-1) {} //! Explicitly create string reference from \c const character pointer -#ifndef __clang__ // -Wdocumentation /*! This constructor can be used to \b explicitly create a reference to a constant string pointer. @@ -298,23 +302,18 @@ struct GenericStringRef { In such cases, the referenced string should be \b copied to the GenericValue instead. */ -#endif explicit GenericStringRef(const CharType* str) - : s(str), length(NotNullStrLen(str)) {} + : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != NULL); } //! Create constant string reference from pointer and length -#ifndef __clang__ // -Wdocumentation /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue \param len length of the string, excluding the trailing NULL terminator \post \ref s == str && \ref length == len \note Constant complexity. */ -#endif GenericStringRef(const CharType* str, SizeType len) - : s(RAPIDJSON_LIKELY(str) ? str : emptyString), length(len) { RAPIDJSON_ASSERT(str != 0 || len == 0u); } - - GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} + : s(str), length(len) { RAPIDJSON_ASSERT(s != NULL); } //! implicit conversion to plain CharType pointer operator const Ch *() const { return s; } @@ -323,24 +322,13 @@ struct GenericStringRef { const SizeType length; //!< length of the string (excluding the trailing NULL terminator) private: - SizeType NotNullStrLen(const CharType* str) { - RAPIDJSON_ASSERT(str != 0); - return internal::StrLen(str); - } - - /// Empty string - used when passing in a NULL pointer - static const Ch emptyString[]; - + //! Disallow copy-assignment + GenericStringRef operator=(const GenericStringRef&); //! Disallow construction from non-const array template GenericStringRef(CharType (&str)[N]) /* = delete */; - //! Copy assignment operator not permitted - immutable type - GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; }; -template -const CharType GenericStringRef::emptyString[] = { CharType() }; - //! Mark a character pointer as constant string /*! Mark a plain character pointer as a "string literal". This function can be used to avoid copying a character string to be referenced as a @@ -355,7 +343,7 @@ const CharType GenericStringRef::emptyString[] = { CharType() }; */ template inline GenericStringRef StringRef(const CharType* str) { - return GenericStringRef(str); + return GenericStringRef(str, internal::StrLen(str)); } //! Mark a character pointer as constant string @@ -413,127 +401,6 @@ template struct IsGenericValue : IsGenericValueImpl::Type {}; } // namespace internal -/////////////////////////////////////////////////////////////////////////////// -// TypeHelper - -namespace internal { - -template -struct TypeHelper {}; - -template -struct TypeHelper { - static bool Is(const ValueType& v) { return v.IsBool(); } - static bool Get(const ValueType& v) { return v.GetBool(); } - static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); } - static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); } -}; - -template -struct TypeHelper { - static bool Is(const ValueType& v) { return v.IsInt(); } - static int Get(const ValueType& v) { return v.GetInt(); } - static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); } - static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); } -}; - -template -struct TypeHelper { - static bool Is(const ValueType& v) { return v.IsUint(); } - static unsigned Get(const ValueType& v) { return v.GetUint(); } - static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); } - static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } -}; - -template -struct TypeHelper { - static bool Is(const ValueType& v) { return v.IsInt64(); } - static int64_t Get(const ValueType& v) { return v.GetInt64(); } - static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); } - static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); } -}; - -template -struct TypeHelper { - static bool Is(const ValueType& v) { return v.IsUint64(); } - static uint64_t Get(const ValueType& v) { return v.GetUint64(); } - static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); } - static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); } -}; - -template -struct TypeHelper { - static bool Is(const ValueType& v) { return v.IsDouble(); } - static double Get(const ValueType& v) { return v.GetDouble(); } - static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); } - static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); } -}; - -template -struct TypeHelper { - static bool Is(const ValueType& v) { return v.IsFloat(); } - static float Get(const ValueType& v) { return v.GetFloat(); } - static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); } - static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); } -}; - -template -struct TypeHelper { - typedef const typename ValueType::Ch* StringType; - static bool Is(const ValueType& v) { return v.IsString(); } - static StringType Get(const ValueType& v) { return v.GetString(); } - static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); } - static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } -}; - -#if RAPIDJSON_HAS_STDSTRING -template -struct TypeHelper > { - typedef std::basic_string StringType; - static bool Is(const ValueType& v) { return v.IsString(); } - static StringType Get(const ValueType& v) { return StringType(v.GetString(), v.GetStringLength()); } - static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } -}; -#endif - -template -struct TypeHelper { - typedef typename ValueType::Array ArrayType; - static bool Is(const ValueType& v) { return v.IsArray(); } - static ArrayType Get(ValueType& v) { return v.GetArray(); } - static ValueType& Set(ValueType& v, ArrayType data) { return v = data; } - static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; } -}; - -template -struct TypeHelper { - typedef typename ValueType::ConstArray ArrayType; - static bool Is(const ValueType& v) { return v.IsArray(); } - static ArrayType Get(const ValueType& v) { return v.GetArray(); } -}; - -template -struct TypeHelper { - typedef typename ValueType::Object ObjectType; - static bool Is(const ValueType& v) { return v.IsObject(); } - static ObjectType Get(ValueType& v) { return v.GetObject(); } - static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } - static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { return v = data; } -}; - -template -struct TypeHelper { - typedef typename ValueType::ConstObject ObjectType; - static bool Is(const ValueType& v) { return v.IsObject(); } - static ObjectType Get(const ValueType& v) { return v.GetObject(); } -}; - -} // namespace internal - -// Forward declarations -template class GenericArray; -template class GenericObject; - /////////////////////////////////////////////////////////////////////////////// // GenericValue @@ -561,21 +428,17 @@ public: typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. typedef GenericValue ValueType; //!< Value type of itself. - typedef GenericArray Array; - typedef GenericArray ConstArray; - typedef GenericObject Object; - typedef GenericObject ConstObject; //!@name Constructors and destructor. //@{ //! Default constructor creates a null value. - GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; } + GenericValue() RAPIDJSON_NOEXCEPT : data_(), flags_(kNullFlag) {} #if RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Move constructor in C++11 - GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) { - rhs.data_.f.flags = kNullFlag; // give up contents + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_), flags_(rhs.flags_) { + rhs.flags_ = kNullFlag; // give up contents } #endif @@ -583,16 +446,6 @@ private: //! Copy constructor is not permitted. GenericValue(const GenericValue& rhs); -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - //! Moving from a GenericDocument is not permitted. - template - GenericValue(GenericDocument&& rhs); - - //! Move assignment from a GenericDocument is not permitted. - template - GenericValue& operator=(GenericDocument&& rhs); -#endif - public: //! Constructor with JSON value type. @@ -600,13 +453,13 @@ public: \param type Type of the value. \note Default content for number is zero. */ - explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() { - static const uint16_t defaultFlags[7] = { + explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_(), flags_() { + static const unsigned defaultFlags[7] = { kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, kNumberAnyFlag }; - RAPIDJSON_ASSERT(type >= kNullType && type <= kNumberType); - data_.f.flags = defaultFlags[type]; + RAPIDJSON_ASSERT(type <= kNumberType); + flags_ = defaultFlags[type]; // Use ShortString to store empty string. if (type == kStringType) @@ -618,50 +471,10 @@ public: \tparam SourceAllocator allocator of \c rhs \param rhs Value to copy from (read-only) \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). - \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) \see CopyFrom() */ - template - GenericValue(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { - switch (rhs.GetType()) { - case kObjectType: { - SizeType count = rhs.data_.o.size; - Member* lm = reinterpret_cast(allocator.Malloc(count * sizeof(Member))); - const typename GenericValue::Member* rm = rhs.GetMembersPointer(); - for (SizeType i = 0; i < count; i++) { - new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings); - new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings); - } - data_.f.flags = kObjectFlag; - data_.o.size = data_.o.capacity = count; - SetMembersPointer(lm); - } - break; - case kArrayType: { - SizeType count = rhs.data_.a.size; - GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); - const GenericValue* re = rhs.GetElementsPointer(); - for (SizeType i = 0; i < count; i++) - new (&le[i]) GenericValue(re[i], allocator, copyConstStrings); - data_.f.flags = kArrayFlag; - data_.a.size = data_.a.capacity = count; - SetElementsPointer(le); - } - break; - case kStringType: - if (rhs.data_.f.flags == kConstStringFlag && !copyConstStrings) { - data_.f.flags = rhs.data_.f.flags; - data_ = *reinterpret_cast(&rhs.data_); - } - else - SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); - break; - default: - data_.f.flags = rhs.data_.f.flags; - data_ = *reinterpret_cast(&rhs.data_); - break; - } - } + template< typename SourceAllocator > + GenericValue(const GenericValue& rhs, Allocator & allocator); //! Constructor for boolean value. /*! \param b Boolean value @@ -671,125 +484,96 @@ public: */ #ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen template - explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT // See #472 + explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT #else explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT #endif - : data_() { + : data_(), flags_(b ? kTrueFlag : kFalseFlag) { // safe-guard against failing SFINAE RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); - data_.f.flags = b ? kTrueFlag : kFalseFlag; } //! Constructor for int value. - explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() { + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberIntFlag) { data_.n.i64 = i; - data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag; + if (i >= 0) + flags_ |= kUintFlag | kUint64Flag; } //! Constructor for unsigned value. - explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() { + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUintFlag) { data_.n.u64 = u; - data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag); + if (!(u & 0x80000000)) + flags_ |= kIntFlag | kInt64Flag; } //! Constructor for int64_t value. - explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() { + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberInt64Flag) { data_.n.i64 = i64; - data_.f.flags = kNumberInt64Flag; if (i64 >= 0) { - data_.f.flags |= kNumberUint64Flag; + flags_ |= kNumberUint64Flag; if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) - data_.f.flags |= kUintFlag; + flags_ |= kUintFlag; if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) - data_.f.flags |= kIntFlag; + flags_ |= kIntFlag; } else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) - data_.f.flags |= kIntFlag; + flags_ |= kIntFlag; } //! Constructor for uint64_t value. - explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() { + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUint64Flag) { data_.n.u64 = u64; - data_.f.flags = kNumberUint64Flag; if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) - data_.f.flags |= kInt64Flag; + flags_ |= kInt64Flag; if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) - data_.f.flags |= kUintFlag; + flags_ |= kUintFlag; if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) - data_.f.flags |= kIntFlag; + flags_ |= kIntFlag; } //! Constructor for double value. - explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; } - - //! Constructor for float value. - explicit GenericValue(float f) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = static_cast(f); data_.f.flags = kNumberDoubleFlag; } + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; } //! Constructor for constant string (i.e. do not make a copy of string) - GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); } + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(StringRef(s, length)); } //! Constructor for constant string (i.e. do not make a copy of string) - explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); } + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(s); } //! Constructor for copy-string (i.e. do make a copy of string) - GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); } + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s, length), allocator); } //! Constructor for copy-string (i.e. do make a copy of string) - GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } + GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } #if RAPIDJSON_HAS_STDSTRING //! Constructor for copy-string from a string object (i.e. do make a copy of string) /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. */ - GenericValue(const std::basic_string& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } + GenericValue(const std::basic_string& s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } #endif - //! Constructor for Array. - /*! - \param a An array obtained by \c GetArray(). - \note \c Array is always pass-by-value. - \note the source array is moved into this value and the sourec array becomes empty. - */ - GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) { - a.value_.data_ = Data(); - a.value_.data_.f.flags = kArrayFlag; - } - - //! Constructor for Object. - /*! - \param o An object obtained by \c GetObject(). - \note \c Object is always pass-by-value. - \note the source object is moved into this value and the sourec object becomes empty. - */ - GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) { - o.value_.data_ = Data(); - o.value_.data_.f.flags = kObjectFlag; - } - //! Destructor. /*! Need to destruct elements of array, members of object, or copy-string. */ ~GenericValue() { if (Allocator::kNeedFree) { // Shortcut by Allocator's trait - switch(data_.f.flags) { + switch(flags_) { case kArrayFlag: - { - GenericValue* e = GetElementsPointer(); - for (GenericValue* v = e; v != e + data_.a.size; ++v) - v->~GenericValue(); - Allocator::Free(e); - } + for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) + v->~GenericValue(); + Allocator::Free(data_.a.elements); break; case kObjectFlag: for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) m->~Member(); - Allocator::Free(GetMembersPointer()); + Allocator::Free(data_.o.members); break; case kCopyStringFlag: - Allocator::Free(const_cast(GetStringPointer())); + Allocator::Free(const_cast(data_.s.str)); break; default: @@ -854,13 +638,12 @@ public: \tparam SourceAllocator Allocator type of \c rhs \param rhs Value to copy from (read-only) \param allocator Allocator to use for copying - \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) */ template - GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { - RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator) { + RAPIDJSON_ASSERT((void*)this != (void const*)&rhs); this->~GenericValue(); - new (this) GenericValue(rhs, allocator, copyConstStrings); + new (this) GenericValue(rhs, allocator); return *this; } @@ -877,20 +660,6 @@ public: return *this; } - //! free-standing swap function helper - /*! - Helper function to enable support for common swap implementation pattern based on \c std::swap: - \code - void swap(MyClass& a, MyClass& b) { - using std::swap; - swap(a.value, b.value); - // ... - } - \endcode - \see Swap() - */ - friend inline void swap(GenericValue& a, GenericValue& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } - //! Prepare Value for move semantics /*! \return *this */ GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } @@ -940,7 +709,7 @@ public: else return data_.n.u64 == rhs.data_.n.u64; - default: + default: // kTrueType, kFalseType, kNullType return true; } } @@ -988,58 +757,20 @@ public: //!@name Type //@{ - Type GetType() const { return static_cast(data_.f.flags & kTypeMask); } - bool IsNull() const { return data_.f.flags == kNullFlag; } - bool IsFalse() const { return data_.f.flags == kFalseFlag; } - bool IsTrue() const { return data_.f.flags == kTrueFlag; } - bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; } - bool IsObject() const { return data_.f.flags == kObjectFlag; } - bool IsArray() const { return data_.f.flags == kArrayFlag; } - bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; } - bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; } - bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; } - bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; } - bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; } - bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; } - bool IsString() const { return (data_.f.flags & kStringFlag) != 0; } - - // Checks whether a number can be losslessly converted to a double. - bool IsLosslessDouble() const { - if (!IsNumber()) return false; - if (IsUint64()) { - uint64_t u = GetUint64(); - volatile double d = static_cast(u); - return (d >= 0.0) - && (d < static_cast((std::numeric_limits::max)())) - && (u == static_cast(d)); - } - if (IsInt64()) { - int64_t i = GetInt64(); - volatile double d = static_cast(i); - return (d >= static_cast((std::numeric_limits::min)())) - && (d < static_cast((std::numeric_limits::max)())) - && (i == static_cast(d)); - } - return true; // double, int, uint are always lossless - } - - // Checks whether a number is a float (possible lossy). - bool IsFloat() const { - if ((data_.f.flags & kDoubleFlag) == 0) - return false; - double d = GetDouble(); - return d >= -3.4028234e38 && d <= 3.4028234e38; - } - // Checks whether a number can be losslessly converted to a float. - bool IsLosslessFloat() const { - if (!IsNumber()) return false; - double a = GetDouble(); - if (a < static_cast(-(std::numeric_limits::max)()) - || a > static_cast((std::numeric_limits::max)())) - return false; - double b = static_cast(static_cast(a)); - return a >= b && a <= b; // Prevent -Wfloat-equal - } + Type GetType() const { return static_cast(flags_ & kTypeMask); } + bool IsNull() const { return flags_ == kNullFlag; } + bool IsFalse() const { return flags_ == kFalseFlag; } + bool IsTrue() const { return flags_ == kTrueFlag; } + bool IsBool() const { return (flags_ & kBoolFlag) != 0; } + bool IsObject() const { return flags_ == kObjectFlag; } + bool IsArray() const { return flags_ == kArrayFlag; } + bool IsNumber() const { return (flags_ & kNumberFlag) != 0; } + bool IsInt() const { return (flags_ & kIntFlag) != 0; } + bool IsUint() const { return (flags_ & kUintFlag) != 0; } + bool IsInt64() const { return (flags_ & kInt64Flag) != 0; } + bool IsUint64() const { return (flags_ & kUint64Flag) != 0; } + bool IsDouble() const { return (flags_ & kDoubleFlag) != 0; } + bool IsString() const { return (flags_ & kStringFlag) != 0; } //@} @@ -1053,7 +784,7 @@ public: //!@name Bool //@{ - bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; } + bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return flags_ == kTrueFlag; } //!< Set boolean value /*! \post IsBool() == true */ GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } @@ -1106,14 +837,8 @@ public: return member->value; else { RAPIDJSON_ASSERT(false); // see above note - - // This will generate -Wexit-time-destructors in clang - // static GenericValue NullValue; - // return NullValue; - - // Use static buffer and placement-new to prevent destruction - static char buffer[sizeof(GenericValue)]; - return *new (buffer) GenericValue(); + static GenericValue NullValue; + return NullValue; } } template @@ -1127,16 +852,16 @@ public: //! Const member iterator /*! \pre IsObject() == true */ - ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); } + ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members); } //! Const \em past-the-end member iterator /*! \pre IsObject() == true */ - ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); } + ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members + data_.o.size); } //! Member iterator /*! \pre IsObject() == true */ - MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); } + MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members); } //! \em Past-the-end member iterator /*! \pre IsObject() == true */ - MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } + MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members + data_.o.size); } //! Check whether a member exists in the object. /*! @@ -1224,8 +949,8 @@ public: \return Iterator to member, if it exists. Otherwise returns \ref MemberEnd(). */ - MemberIterator FindMember(const std::basic_string& name) { return FindMember(GenericValue(StringRef(name))); } - ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(GenericValue(StringRef(name))); } + MemberIterator FindMember(const std::basic_string& name) { return FindMember(StringRef(name)); } + ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(StringRef(name)); } #endif //! Add a member (name-value pair) to the object. @@ -1242,21 +967,20 @@ public: RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(name.IsString()); - ObjectData& o = data_.o; + Object& o = data_.o; if (o.size >= o.capacity) { if (o.capacity == 0) { o.capacity = kDefaultObjectCapacity; - SetMembersPointer(reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member)))); + o.members = reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member))); } else { SizeType oldCapacity = o.capacity; o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5 - SetMembersPointer(reinterpret_cast(allocator.Realloc(GetMembersPointer(), oldCapacity * sizeof(Member), o.capacity * sizeof(Member)))); + o.members = reinterpret_cast(allocator.Realloc(o.members, oldCapacity * sizeof(Member), o.capacity * sizeof(Member))); } } - Member* members = GetMembersPointer(); - members[o.size].name.RawAssign(name); - members[o.size].value.RawAssign(value); + o.members[o.size].name.RawAssign(name); + o.members[o.size].value.RawAssign(value); o.size++; return *this; } @@ -1435,14 +1159,18 @@ public: MemberIterator RemoveMember(MemberIterator m) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(data_.o.size > 0); - RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(data_.o.members != 0); RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); - MemberIterator last(GetMembersPointer() + (data_.o.size - 1)); - if (data_.o.size > 1 && m != last) - *m = *last; // Move the last one to this place - else - m->~Member(); // Only one left, just destroy + MemberIterator last(data_.o.members + (data_.o.size - 1)); + if (data_.o.size > 1 && m != last) { + // Move the last one to this place + *m = *last; + } + else { + // Only one left, just destroy + m->~Member(); + } --data_.o.size; return m; } @@ -1472,7 +1200,7 @@ public: MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(data_.o.size > 0); - RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(data_.o.members != 0); RAPIDJSON_ASSERT(first >= MemberBegin()); RAPIDJSON_ASSERT(first <= last); RAPIDJSON_ASSERT(last <= MemberEnd()); @@ -1480,39 +1208,11 @@ public: MemberIterator pos = MemberBegin() + (first - MemberBegin()); for (MemberIterator itr = pos; itr != last; ++itr) itr->~Member(); - std::memmove(&*pos, &*last, static_cast(MemberEnd() - last) * sizeof(Member)); - data_.o.size -= static_cast(last - first); + std::memmove(&*pos, &*last, (MemberEnd() - last) * sizeof(Member)); + data_.o.size -= (last - first); return pos; } - //! Erase a member in object by its name. - /*! \param name Name of member to be removed. - \return Whether the member existed. - \note Linear time complexity. - */ - bool EraseMember(const Ch* name) { - GenericValue n(StringRef(name)); - return EraseMember(n); - } - -#if RAPIDJSON_HAS_STDSTRING - bool EraseMember(const std::basic_string& name) { return EraseMember(GenericValue(StringRef(name))); } -#endif - - template - bool EraseMember(const GenericValue& name) { - MemberIterator m = FindMember(name); - if (m != MemberEnd()) { - EraseMember(m); - return true; - } - else - return false; - } - - Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } - ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } - //@} //!@name Array @@ -1520,7 +1220,7 @@ public: //! Set this value as an empty array. /*! \post IsArray == true */ - GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } + GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } //! Get the number of elements in array. SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } @@ -1537,9 +1237,8 @@ public: */ void Clear() { RAPIDJSON_ASSERT(IsArray()); - GenericValue* e = GetElementsPointer(); - for (GenericValue* v = e; v != e + data_.a.size; ++v) - v->~GenericValue(); + for (SizeType i = 0; i < data_.a.size; ++i) + data_.a.elements[i].~GenericValue(); data_.a.size = 0; } @@ -1551,16 +1250,16 @@ public: GenericValue& operator[](SizeType index) { RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(index < data_.a.size); - return GetElementsPointer()[index]; + return data_.a.elements[index]; } const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } //! Element iterator /*! \pre IsArray() == true */ - ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); } + ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements; } //! \em Past-the-end element iterator /*! \pre IsArray() == true */ - ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; } + ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements + data_.a.size; } //! Constant element iterator /*! \pre IsArray() == true */ ConstValueIterator Begin() const { return const_cast(*this).Begin(); } @@ -1577,7 +1276,7 @@ public: GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { RAPIDJSON_ASSERT(IsArray()); if (newCapacity > data_.a.capacity) { - SetElementsPointer(reinterpret_cast(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)))); + data_.a.elements = (GenericValue*)allocator.Realloc(data_.a.elements, data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)); data_.a.capacity = newCapacity; } return *this; @@ -1597,7 +1296,7 @@ public: RAPIDJSON_ASSERT(IsArray()); if (data_.a.size >= data_.a.capacity) Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); - GetElementsPointer()[data_.a.size++].RawAssign(value); + data_.a.elements[data_.a.size++].RawAssign(value); return *this; } @@ -1651,7 +1350,7 @@ public: GenericValue& PopBack() { RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(!Empty()); - GetElementsPointer()[--data_.a.size].~GenericValue(); + data_.a.elements[--data_.a.size].~GenericValue(); return *this; } @@ -1677,48 +1376,35 @@ public: ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(data_.a.size > 0); - RAPIDJSON_ASSERT(GetElementsPointer() != 0); + RAPIDJSON_ASSERT(data_.a.elements != 0); RAPIDJSON_ASSERT(first >= Begin()); RAPIDJSON_ASSERT(first <= last); RAPIDJSON_ASSERT(last <= End()); ValueIterator pos = Begin() + (first - Begin()); for (ValueIterator itr = pos; itr != last; ++itr) itr->~GenericValue(); - std::memmove(pos, last, static_cast(End() - last) * sizeof(GenericValue)); - data_.a.size -= static_cast(last - first); + std::memmove(pos, last, (End() - last) * sizeof(GenericValue)); + data_.a.size -= (last - first); return pos; } - Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); } - ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); } - //@} //!@name Number //@{ - int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; } - unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; } - int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; } - uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; } + int GetInt() const { RAPIDJSON_ASSERT(flags_ & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { RAPIDJSON_ASSERT(flags_ & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { RAPIDJSON_ASSERT(flags_ & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { RAPIDJSON_ASSERT(flags_ & kUint64Flag); return data_.n.u64; } - //! Get the value as double type. - /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless. - */ double GetDouble() const { RAPIDJSON_ASSERT(IsNumber()); - if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. - if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double - if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double - if ((data_.f.flags & kInt64Flag) != 0) return static_cast(data_.n.i64); // int64_t -> double (may lose precision) - RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast(data_.n.u64); // uint64_t -> double (may lose precision) - } - - //! Get the value as float type. - /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless. - */ - float GetFloat() const { - return static_cast(GetDouble()); + if ((flags_ & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((flags_ & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((flags_ & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((flags_ & kInt64Flag) != 0) return (double)data_.n.i64; // int64_t -> double (may lose precision) + RAPIDJSON_ASSERT((flags_ & kUint64Flag) != 0); return (double)data_.n.u64; // uint64_t -> double (may lose precision) } GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } @@ -1726,19 +1412,18 @@ public: GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } - GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(static_cast(f)); return *this; } //@} //!@name String //@{ - const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); } + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? data_.ss.str : data_.s.str); } //! Get the length of string. /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). */ - SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } //! Set this value as a string without copying source string. /*! This version has better performance with supplied length, and also support string containing null character. @@ -1765,7 +1450,7 @@ public: \return The value itself for fluent API. \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length */ - GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { return SetString(StringRef(s, length), allocator); } + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; } //! Set this value as a string by copying from source string. /*! \param s source string. @@ -1773,15 +1458,7 @@ public: \return The value itself for fluent API. \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length */ - GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(StringRef(s), allocator); } - - //! Set this value as a string by copying from source string. - /*! \param s source string reference - \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \post IsString() == true && GetString() != s.s && strcmp(GetString(),s) == 0 && GetStringLength() == length - */ - GenericValue& SetString(StringRefType s, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, allocator); return *this; } + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } #if RAPIDJSON_HAS_STDSTRING //! Set this value as a string by copying from source string. @@ -1791,35 +1468,11 @@ public: \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. */ - GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(StringRef(s), allocator); } + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(s.data(), SizeType(s.size()), allocator); } #endif //@} - //!@name Array - //@{ - - //! Templated version for checking whether this value is type T. - /*! - \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string - */ - template - bool Is() const { return internal::TypeHelper::Is(*this); } - - template - T Get() const { return internal::TypeHelper::Get(*this); } - - template - T Get() { return internal::TypeHelper::Get(*this); } - - template - ValueType& Set(const T& data) { return internal::TypeHelper::Set(*this, data); } - - template - ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper::Set(*this, data, allocator); } - - //@} - //! Generate events of this value to a Handler. /*! This function adopts the GoF visitor pattern. Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. @@ -1835,35 +1488,35 @@ public: case kTrueType: return handler.Bool(true); case kObjectType: - if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + if (!handler.StartObject()) return false; for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. - if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0))) + if (!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.flags_ & kCopyFlag) != 0)) return false; - if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler))) + if (!m->value.Accept(handler)) return false; } return handler.EndObject(data_.o.size); case kArrayType: - if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + if (!handler.StartArray()) return false; - for (const GenericValue* v = Begin(); v != End(); ++v) - if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) + for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) + if (!v->Accept(handler)) return false; return handler.EndArray(data_.a.size); case kStringType: - return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0); + return handler.String(GetString(), GetStringLength(), (flags_ & kCopyFlag) != 0); default: RAPIDJSON_ASSERT(GetType() == kNumberType); - if (IsDouble()) return handler.Double(data_.n.d); - else if (IsInt()) return handler.Int(data_.n.i.i); + if (IsInt()) return handler.Int(data_.n.i.i); else if (IsUint()) return handler.Uint(data_.n.u.u); else if (IsInt64()) return handler.Int64(data_.n.i64); - else return handler.Uint64(data_.n.u64); + else if (IsUint64()) return handler.Uint64(data_.n.u64); + else return handler.Double(data_.n.d); } } @@ -1872,16 +1525,16 @@ private: template friend class GenericDocument; enum { - kBoolFlag = 0x0008, - kNumberFlag = 0x0010, - kIntFlag = 0x0020, - kUintFlag = 0x0040, - kInt64Flag = 0x0080, - kUint64Flag = 0x0100, - kDoubleFlag = 0x0200, - kStringFlag = 0x0400, - kCopyFlag = 0x0800, - kInlineStrFlag = 0x1000, + kBoolFlag = 0x100, + kNumberFlag = 0x200, + kIntFlag = 0x400, + kUintFlag = 0x800, + kInt64Flag = 0x1000, + kUint64Flag = 0x2000, + kDoubleFlag = 0x4000, + kStringFlag = 0x100000, + kCopyFlag = 0x200000, + kInlineStrFlag = 0x400000, // Initial flags of different types. kNullFlag = kNullType, @@ -1899,27 +1552,16 @@ private: kObjectFlag = kObjectType, kArrayFlag = kArrayType, - kTypeMask = 0x07 + kTypeMask = 0xFF // bitwise-and with mask of 0xFF can be optimized by compiler }; static const SizeType kDefaultArrayCapacity = 16; static const SizeType kDefaultObjectCapacity = 16; - struct Flag { -#if RAPIDJSON_48BITPOINTER_OPTIMIZATION - char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer -#elif RAPIDJSON_64BIT - char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes -#else - char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes -#endif - uint16_t flags; - }; - struct String { - SizeType length; - SizeType hashcode; //!< reserved const Ch* str; + SizeType length; + unsigned hashcode; //!< reserved }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars @@ -1928,15 +1570,15 @@ private: // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as // the string terminator as well. For getting the string length back from that value just use // "MaxSize - str[LenPos]". - // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode, - // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings). + // This allows to store 11-chars strings in 32-bit mode and 15-chars strings in 64-bit mode + // inline (for `UTF8`-encoded strings). struct ShortString { - enum { MaxChars = sizeof(static_cast(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + enum { MaxChars = sizeof(String) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; Ch str[MaxChars]; - inline static bool Usable(SizeType len) { return (MaxSize >= len); } - inline void SetLength(SizeType len) { str[LenPos] = static_cast(MaxSize - len); } - inline SizeType GetLength() const { return static_cast(MaxSize - str[LenPos]); } + inline static bool Usable(SizeType len) { return (MaxSize >= len); } + inline void SetLength(SizeType len) { str[LenPos] = (Ch)(MaxSize - len); } + inline SizeType GetLength() const { return (SizeType)(MaxSize - str[LenPos]); } }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode // By using proper binary layout, retrieval of different integer types do not need conversions. @@ -1965,79 +1607,69 @@ private: double d; }; // 8 bytes - struct ObjectData { + struct Object { + Member* members; SizeType size; SizeType capacity; - Member* members; }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode - struct ArrayData { + struct Array { + GenericValue* elements; SizeType size; SizeType capacity; - GenericValue* elements; }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode union Data { String s; ShortString ss; Number n; - ObjectData o; - ArrayData a; - Flag f; - }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION - - RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); } - RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); } - RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); } - RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } - RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); } - RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } + Object o; + Array a; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode // Initialize this value as array with initial data, without calling destructor. void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { - data_.f.flags = kArrayFlag; + flags_ = kArrayFlag; if (count) { - GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); - SetElementsPointer(e); - std::memcpy(e, values, count * sizeof(GenericValue)); + data_.a.elements = (GenericValue*)allocator.Malloc(count * sizeof(GenericValue)); + std::memcpy(data_.a.elements, values, count * sizeof(GenericValue)); } else - SetElementsPointer(0); + data_.a.elements = NULL; data_.a.size = data_.a.capacity = count; } //! Initialize this value as object with initial data, without calling destructor. void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { - data_.f.flags = kObjectFlag; + flags_ = kObjectFlag; if (count) { - Member* m = static_cast(allocator.Malloc(count * sizeof(Member))); - SetMembersPointer(m); - std::memcpy(m, members, count * sizeof(Member)); + data_.o.members = (Member*)allocator.Malloc(count * sizeof(Member)); + std::memcpy(data_.o.members, members, count * sizeof(Member)); } else - SetMembersPointer(0); + data_.o.members = NULL; data_.o.size = data_.o.capacity = count; } //! Initialize this value as constant string, without calling destructor. void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { - data_.f.flags = kConstStringFlag; - SetStringPointer(s); + flags_ = kConstStringFlag; + data_.s.str = s; data_.s.length = s.length; } //! Initialize this value as copy string with initial data, without calling destructor. void SetStringRaw(StringRefType s, Allocator& allocator) { - Ch* str = 0; - if (ShortString::Usable(s.length)) { - data_.f.flags = kShortStringFlag; + Ch* str = NULL; + if(ShortString::Usable(s.length)) { + flags_ = kShortStringFlag; data_.ss.SetLength(s.length); str = data_.ss.str; } else { - data_.f.flags = kCopyStringFlag; + flags_ = kCopyStringFlag; data_.s.length = s.length; - str = static_cast(allocator.Malloc((s.length + 1) * sizeof(Ch))); - SetStringPointer(str); + str = (Ch *)allocator.Malloc((s.length + 1) * sizeof(Ch)); + data_.s.str = str; } std::memcpy(str, s, s.length * sizeof(Ch)); str[s.length] = '\0'; @@ -2046,8 +1678,8 @@ private: //! Assignment without calling destructor void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { data_ = rhs.data_; - // data_.f.flags = rhs.data_.f.flags; - rhs.data_.f.flags = kNullFlag; + flags_ = rhs.flags_; + rhs.flags_ = kNullFlag; } template @@ -2067,6 +1699,7 @@ private: } Data data_; + unsigned flags_; }; //! GenericValue with UTF8 encoding @@ -2091,22 +1724,7 @@ public: typedef Allocator AllocatorType; //!< Allocator type from template parameter. //! Constructor - /*! Creates an empty document of specified type. - \param type Mandatory type of object to create. - \param allocator Optional allocator for allocating memory. - \param stackCapacity Optional initial capacity of stack in bytes. - \param stackAllocator Optional allocator for allocating memory for stack. - */ - explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : - GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() - { - if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); - } - - //! Constructor - /*! Creates an empty document which type is Null. - \param allocator Optional allocator for allocating memory. + /*! \param allocator Optional allocator for allocating memory. \param stackCapacity Optional initial capacity of stack in bytes. \param stackAllocator Optional allocator for allocating memory for stack. */ @@ -2114,13 +1732,13 @@ public: allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Move constructor in C++11 GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT - : ValueType(std::forward(rhs)), // explicit cast to avoid prohibited move from Document + : ValueType(std::move(rhs)), allocator_(rhs.allocator_), ownAllocator_(rhs.ownAllocator_), stack_(std::move(rhs.stack_)), @@ -2160,54 +1778,6 @@ public: } #endif - //! Exchange the contents of this document with those of another. - /*! - \param rhs Another document. - \note Constant complexity. - \see GenericValue::Swap - */ - GenericDocument& Swap(GenericDocument& rhs) RAPIDJSON_NOEXCEPT { - ValueType::Swap(rhs); - stack_.Swap(rhs.stack_); - internal::Swap(allocator_, rhs.allocator_); - internal::Swap(ownAllocator_, rhs.ownAllocator_); - internal::Swap(parseResult_, rhs.parseResult_); - return *this; - } - - // Allow Swap with ValueType. - // Refer to Effective C++ 3rd Edition/Item 33: Avoid hiding inherited names. - using ValueType::Swap; - - //! free-standing swap function helper - /*! - Helper function to enable support for common swap implementation pattern based on \c std::swap: - \code - void swap(MyClass& a, MyClass& b) { - using std::swap; - swap(a.doc, b.doc); - // ... - } - \endcode - \see Swap() - */ - friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } - - //! Populate this document by a generator which produces SAX events. - /*! \tparam Generator A functor with bool f(Handler) prototype. - \param g Generator functor which sends SAX events to the parameter. - \return The document itself for fluent API. - */ - template - GenericDocument& Populate(Generator& g) { - ClearStackOnExit scope(*this); - if (g(*this)) { - RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object - ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document - } - return *this; - } - //!@name Parse from stream //!@{ @@ -2220,13 +1790,13 @@ public: */ template GenericDocument& ParseStream(InputStream& is) { - GenericReader reader( - stack_.HasAllocator() ? &stack_.GetAllocator() : 0); + ValueType::SetNull(); // Remove existing root if exist + GenericReader reader(&stack_.GetAllocator()); ClearStackOnExit scope(*this); parseResult_ = reader.template Parse(is, *this); if (parseResult_) { RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object - ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + this->RawAssign(*stack_.template Pop(1)); // Add this-> to prevent issue 13. } return *this; } @@ -2285,7 +1855,7 @@ public: \param str Read-only zero-terminated string to be parsed. */ template - GenericDocument& Parse(const typename SourceEncoding::Ch* str) { + GenericDocument& Parse(const Ch* str) { RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); GenericStringStream s(str); return ParseStream(s); @@ -2306,42 +1876,6 @@ public: GenericDocument& Parse(const Ch* str) { return Parse(str); } - - template - GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { - RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); - MemoryStream ms(reinterpret_cast(str), length * sizeof(typename SourceEncoding::Ch)); - EncodedInputStream is(ms); - ParseStream(is); - return *this; - } - - template - GenericDocument& Parse(const Ch* str, size_t length) { - return Parse(str, length); - } - - GenericDocument& Parse(const Ch* str, size_t length) { - return Parse(str, length); - } - -#if RAPIDJSON_HAS_STDSTRING - template - GenericDocument& Parse(const std::basic_string& str) { - // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t) - return Parse(str.c_str()); - } - - template - GenericDocument& Parse(const std::basic_string& str) { - return Parse(str.c_str()); - } - - GenericDocument& Parse(const std::basic_string& str) { - return Parse(str); - } -#endif // RAPIDJSON_HAS_STDSTRING - //!@} //!@name Handling parse errors @@ -2356,26 +1890,10 @@ public: //! Get the position of last parsing error in input, 0 otherwise. size_t GetErrorOffset() const { return parseResult_.Offset(); } - //! Implicit conversion to get the last parse result -#ifndef __clang // -Wdocumentation - /*! \return \ref ParseResult of the last parse operation - - \code - Document doc; - ParseResult ok = doc.Parse(json); - if (!ok) - printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset()); - \endcode - */ -#endif - operator ParseResult() const { return parseResult_; } //!@} //! Get the allocator of this document. - Allocator& GetAllocator() { - RAPIDJSON_ASSERT(allocator_); - return *allocator_; - } + Allocator& GetAllocator() { return *allocator_; } //! Get the capacity of stack in bytes. size_t GetStackCapacity() const { return stack_.GetCapacity(); } @@ -2392,10 +1910,9 @@ private: }; // callers of the following private Handler functions - // template friend class GenericReader; // for parsing + template friend class GenericReader; // for parsing template friend class GenericValue; // for deep copying -public: // Implementation of Handler bool Null() { new (stack_.template Push()) ValueType(); return true; } bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } @@ -2405,14 +1922,6 @@ public: bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } - bool RawNumber(const Ch* str, SizeType length, bool copy) { - if (copy) - new (stack_.template Push()) ValueType(str, length, GetAllocator()); - else - new (stack_.template Push()) ValueType(str, length); - return true; - } - bool String(const Ch* str, SizeType length, bool copy) { if (copy) new (stack_.template Push()) ValueType(str, length, GetAllocator()); @@ -2427,7 +1936,7 @@ public: bool EndObject(SizeType memberCount) { typename ValueType::Member* members = stack_.template Pop(memberCount); - stack_.template Top()->SetObjectRaw(members, memberCount, GetAllocator()); + stack_.template Top()->SetObjectRaw(members, (SizeType)memberCount, GetAllocator()); return true; } @@ -2468,146 +1977,38 @@ private: //! GenericDocument with UTF8 encoding typedef GenericDocument > Document; -//! Helper class for accessing Value of array type. -/*! - Instance of this helper class is obtained by \c GenericValue::GetArray(). - In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. -*/ -template -class GenericArray { -public: - typedef GenericArray ConstArray; - typedef GenericArray Array; - typedef ValueT PlainType; - typedef typename internal::MaybeAddConst::Type ValueType; - typedef ValueType* ValueIterator; // This may be const or non-const iterator - typedef const ValueT* ConstValueIterator; - typedef typename ValueType::AllocatorType AllocatorType; - typedef typename ValueType::StringRefType StringRefType; - - template - friend class GenericValue; - - GenericArray(const GenericArray& rhs) : value_(rhs.value_) {} - GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } - ~GenericArray() {} - - SizeType Size() const { return value_.Size(); } - SizeType Capacity() const { return value_.Capacity(); } - bool Empty() const { return value_.Empty(); } - void Clear() const { value_.Clear(); } - ValueType& operator[](SizeType index) const { return value_[index]; } - ValueIterator Begin() const { return value_.Begin(); } - ValueIterator End() const { return value_.End(); } - GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; } - GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } -#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } - template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } - GenericArray PopBack() const { value_.PopBack(); return *this; } - ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); } - ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); } - -#if RAPIDJSON_HAS_CXX11_RANGE_FOR - ValueIterator begin() const { return value_.Begin(); } - ValueIterator end() const { return value_.End(); } -#endif - -private: - GenericArray(); - GenericArray(ValueType& value) : value_(value) {} - ValueType& value_; -}; - -//! Helper class for accessing Value of object type. -/*! - Instance of this helper class is obtained by \c GenericValue::GetObject(). - In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. -*/ -template -class GenericObject { -public: - typedef GenericObject ConstObject; - typedef GenericObject Object; - typedef ValueT PlainType; - typedef typename internal::MaybeAddConst::Type ValueType; - typedef GenericMemberIterator MemberIterator; // This may be const or non-const iterator - typedef GenericMemberIterator ConstMemberIterator; - typedef typename ValueType::AllocatorType AllocatorType; - typedef typename ValueType::StringRefType StringRefType; - typedef typename ValueType::EncodingType EncodingType; - typedef typename ValueType::Ch Ch; - - template - friend class GenericValue; - - GenericObject(const GenericObject& rhs) : value_(rhs.value_) {} - GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } - ~GenericObject() {} - - SizeType MemberCount() const { return value_.MemberCount(); } - bool ObjectEmpty() const { return value_.ObjectEmpty(); } - template ValueType& operator[](T* name) const { return value_[name]; } - template ValueType& operator[](const GenericValue& name) const { return value_[name]; } -#if RAPIDJSON_HAS_STDSTRING - ValueType& operator[](const std::basic_string& name) const { return value_[name]; } -#endif - MemberIterator MemberBegin() const { return value_.MemberBegin(); } - MemberIterator MemberEnd() const { return value_.MemberEnd(); } - bool HasMember(const Ch* name) const { return value_.HasMember(name); } -#if RAPIDJSON_HAS_STDSTRING - bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } -#endif - template bool HasMember(const GenericValue& name) const { return value_.HasMember(name); } - MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); } - template MemberIterator FindMember(const GenericValue& name) const { return value_.FindMember(name); } -#if RAPIDJSON_HAS_STDSTRING - MemberIterator FindMember(const std::basic_string& name) const { return value_.FindMember(name); } -#endif - GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } - GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } -#if RAPIDJSON_HAS_STDSTRING - GenericObject AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } -#endif - template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } - GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } - GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } - GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } -#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } - GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } - template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } - void RemoveAllMembers() { value_.RemoveAllMembers(); } - bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } -#if RAPIDJSON_HAS_STDSTRING - bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } -#endif - template bool RemoveMember(const GenericValue& name) const { return value_.RemoveMember(name); } - MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); } - MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); } - MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); } - bool EraseMember(const Ch* name) const { return value_.EraseMember(name); } -#if RAPIDJSON_HAS_STDSTRING - bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } -#endif - template bool EraseMember(const GenericValue& name) const { return value_.EraseMember(name); } - -#if RAPIDJSON_HAS_CXX11_RANGE_FOR - MemberIterator begin() const { return value_.MemberBegin(); } - MemberIterator end() const { return value_.MemberEnd(); } -#endif - -private: - GenericObject(); - GenericObject(ValueType& value) : value_(value) {} - ValueType& value_; -}; +// defined here due to the dependency on GenericDocument +template +template +inline +GenericValue::GenericValue(const GenericValue& rhs, Allocator& allocator) +{ + switch (rhs.GetType()) { + case kObjectType: + case kArrayType: { // perform deep copy via SAX Handler + GenericDocument d(&allocator); + rhs.Accept(d); + RawAssign(*d.stack_.template Pop(1)); + } + break; + case kStringType: + if (rhs.flags_ == kConstStringFlag) { + flags_ = rhs.flags_; + data_ = *reinterpret_cast(&rhs.data_); + } else { + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + } + break; + default: // kNumberType, kTrueType, kFalseType, kNullType + flags_ = rhs.flags_; + data_ = *reinterpret_cast(&rhs.data_); + } +} RAPIDJSON_NAMESPACE_END + +#if defined(_MSC_VER) || defined(__GNUC__) RAPIDJSON_DIAG_POP +#endif #endif // RAPIDJSON_DOCUMENT_H_ diff --git a/contrib/rapidjson/include/rapidjson/encodedstream.h b/contrib/rapidjson/include/rapidjson/encodedstream.h index 223601c05..7c8863fee 100644 --- a/contrib/rapidjson/include/rapidjson/encodedstream.h +++ b/contrib/rapidjson/include/rapidjson/encodedstream.h @@ -15,19 +15,13 @@ #ifndef RAPIDJSON_ENCODEDSTREAM_H_ #define RAPIDJSON_ENCODEDSTREAM_H_ -#include "stream.h" -#include "memorystream.h" +#include "rapidjson.h" #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(padded) -#endif - RAPIDJSON_NAMESPACE_BEGIN //! Input byte stream wrapper with a statically bound encoding. @@ -63,38 +57,10 @@ private: Ch current_; }; -//! Specialized for UTF8 MemoryStream. -template <> -class EncodedInputStream, MemoryStream> { -public: - typedef UTF8<>::Ch Ch; - - EncodedInputStream(MemoryStream& is) : is_(is) { - if (static_cast(is_.Peek()) == 0xEFu) is_.Take(); - if (static_cast(is_.Peek()) == 0xBBu) is_.Take(); - if (static_cast(is_.Peek()) == 0xBFu) is_.Take(); - } - Ch Peek() const { return is_.Peek(); } - Ch Take() { return is_.Take(); } - size_t Tell() const { return is_.Tell(); } - - // Not implemented - void Put(Ch) {} - void Flush() {} - Ch* PutBegin() { return 0; } - size_t PutEnd(Ch*) { return 0; } - - MemoryStream& is_; - -private: - EncodedInputStream(const EncodedInputStream&); - EncodedInputStream& operator=(const EncodedInputStream&); -}; - //! Output byte stream wrapper with statically bound encoding. /*! \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. - \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream. + \tparam InputByteStream Type of input byte stream. For example, FileWriteStream. */ template class EncodedOutputStream { @@ -111,8 +77,8 @@ public: void Flush() { os_.Flush(); } // Not implemented - Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} - Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + Ch Peek() const { RAPIDJSON_ASSERT(false); } + Ch Take() { RAPIDJSON_ASSERT(false); } size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } @@ -176,11 +142,11 @@ private: // FF FE UTF-16LE // EF BB BF UTF-8 - const unsigned char* c = reinterpret_cast(is_->Peek4()); + const unsigned char* c = (const unsigned char *)is_->Peek4(); if (!c) return; - unsigned bom = static_cast(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)); + unsigned bom = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24); hasBOM_ = false; if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } @@ -200,7 +166,7 @@ private: // xx xx xx xx UTF-8 if (!hasBOM_) { - int pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); switch (pattern) { case 0x08: type_ = kUTF32BE; break; case 0x0A: type_ = kUTF16BE; break; @@ -227,7 +193,7 @@ private: //! Output stream wrapper with dynamically bound encoding and automatic encoding detection. /*! \tparam CharType Type of character for writing. - \tparam OutputByteStream type of output byte stream to be wrapped. + \tparam InputByteStream type of output byte stream to be wrapped. */ template class AutoUTFOutputStream { @@ -261,8 +227,8 @@ public: void Flush() { os_->Flush(); } // Not implemented - Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} - Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + Ch Peek() const { RAPIDJSON_ASSERT(false); } + Ch Take() { RAPIDJSON_ASSERT(false); } size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } @@ -288,10 +254,6 @@ private: RAPIDJSON_NAMESPACE_END -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif diff --git a/contrib/rapidjson/include/rapidjson/encodings.h b/contrib/rapidjson/include/rapidjson/encodings.h index 0df1c3435..90b46ed32 100644 --- a/contrib/rapidjson/include/rapidjson/encodings.h +++ b/contrib/rapidjson/include/rapidjson/encodings.h @@ -120,45 +120,19 @@ struct UTF8 { } } - template - static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { - if (codepoint <= 0x7F) - PutUnsafe(os, static_cast(codepoint & 0xFF)); - else if (codepoint <= 0x7FF) { - PutUnsafe(os, static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); - PutUnsafe(os, static_cast(0x80 | ((codepoint & 0x3F)))); - } - else if (codepoint <= 0xFFFF) { - PutUnsafe(os, static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); - PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); - PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); - } - else { - RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); - PutUnsafe(os, static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); - PutUnsafe(os, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); - PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); - PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); - } - } - template static bool Decode(InputStream& is, unsigned* codepoint) { -#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) -#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | ((unsigned char)c & 0x3Fu) +#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0) #define TAIL() COPY(); TRANS(0x70) - typename InputStream::Ch c = is.Take(); + Ch c = is.Take(); if (!(c & 0x80)) { - *codepoint = static_cast(c); + *codepoint = (unsigned char)c; return true; } - unsigned char type = GetRange(static_cast(c)); - if (type >= 32) { - *codepoint = 0; - } else { - *codepoint = (0xFFu >> type) & static_cast(c); - } + unsigned char type = GetRange((unsigned char)c); + *codepoint = (0xFF >> type) & (unsigned char)c; bool result = true; switch (type) { case 2: TAIL(); return result; @@ -178,7 +152,7 @@ struct UTF8 { template static bool Validate(InputStream& is, OutputStream& os) { #define COPY() os.Put(c = is.Take()) -#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0) #define TAIL() COPY(); TRANS(0x70) Ch c; COPY(); @@ -186,7 +160,7 @@ struct UTF8 { return true; bool result = true; - switch (GetRange(static_cast(c))) { + switch (GetRange((unsigned char)c)) { case 2: TAIL(); return result; case 3: TAIL(); TAIL(); return result; case 4: COPY(); TRANS(0x50); TAIL(); return result; @@ -222,12 +196,12 @@ struct UTF8 { template static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - typename InputByteStream::Ch c = Take(is); - if (static_cast(c) != 0xEFu) return c; + Ch c = Take(is); + if ((unsigned char)c != 0xEFu) return c; c = is.Take(); - if (static_cast(c) != 0xBBu) return c; + if ((unsigned char)c != 0xBBu) return c; c = is.Take(); - if (static_cast(c) != 0xBFu) return c; + if ((unsigned char)c != 0xBFu) return c; c = is.Take(); return c; } @@ -235,15 +209,13 @@ struct UTF8 { template static Ch Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - return static_cast(is.Take()); + return is.Take(); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast(0xEFu)); - os.Put(static_cast(0xBBu)); - os.Put(static_cast(0xBFu)); + os.Put(0xEFu); os.Put(0xBBu); os.Put(0xBFu); } template @@ -283,38 +255,22 @@ struct UTF16 { RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); unsigned v = codepoint - 0x10000; os.Put(static_cast((v >> 10) | 0xD800)); - os.Put(static_cast((v & 0x3FF) | 0xDC00)); - } - } - - - template - static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); - if (codepoint <= 0xFFFF) { - RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair - PutUnsafe(os, static_cast(codepoint)); - } - else { - RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); - unsigned v = codepoint - 0x10000; - PutUnsafe(os, static_cast((v >> 10) | 0xD800)); - PutUnsafe(os, static_cast((v & 0x3FF) | 0xDC00)); + os.Put((v & 0x3FF) | 0xDC00); } } template static bool Decode(InputStream& is, unsigned* codepoint) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); - typename InputStream::Ch c = is.Take(); + Ch c = is.Take(); if (c < 0xD800 || c > 0xDFFF) { - *codepoint = static_cast(c); + *codepoint = c; return true; } else if (c <= 0xDBFF) { - *codepoint = (static_cast(c) & 0x3FF) << 10; + *codepoint = (c & 0x3FF) << 10; c = is.Take(); - *codepoint |= (static_cast(c) & 0x3FF); + *codepoint |= (c & 0x3FF); *codepoint += 0x10000; return c >= 0xDC00 && c <= 0xDFFF; } @@ -325,8 +281,8 @@ struct UTF16 { static bool Validate(InputStream& is, OutputStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); - typename InputStream::Ch c; - os.Put(static_cast(c = is.Take())); + Ch c; + os.Put(c = is.Take()); if (c < 0xD800 || c > 0xDFFF) return true; else if (c <= 0xDBFF) { @@ -344,29 +300,28 @@ struct UTF16LE : UTF16 { static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); - return static_cast(c) == 0xFEFFu ? Take(is) : c; + return (unsigned short)c == 0xFEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - unsigned c = static_cast(is.Take()); - c |= static_cast(static_cast(is.Take())) << 8; - return static_cast(c); + CharType c = (unsigned char)is.Take(); + c |= (unsigned char)is.Take() << 8; + return c; } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast(0xFFu)); - os.Put(static_cast(0xFEu)); + os.Put(0xFFu); os.Put(0xFEu); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast(static_cast(c) & 0xFFu)); - os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + os.Put(c & 0xFFu); + os.Put((c >> 8) & 0xFFu); } }; @@ -377,29 +332,28 @@ struct UTF16BE : UTF16 { static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); - return static_cast(c) == 0xFEFFu ? Take(is) : c; + return (unsigned short)c == 0xFEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - unsigned c = static_cast(static_cast(is.Take())) << 8; - c |= static_cast(is.Take()); - return static_cast(c); + CharType c = (unsigned char)is.Take() << 8; + c |= (unsigned char)is.Take(); + return c; } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast(0xFEu)); - os.Put(static_cast(0xFFu)); + os.Put(0xFEu); os.Put(0xFFu); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); - os.Put(static_cast(static_cast(c) & 0xFFu)); + os.Put((c >> 8) & 0xFFu); + os.Put(c & 0xFFu); } }; @@ -428,13 +382,6 @@ struct UTF32 { os.Put(codepoint); } - template - static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); - RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); - PutUnsafe(os, codepoint); - } - template static bool Decode(InputStream& is, unsigned* codepoint) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); @@ -459,35 +406,32 @@ struct UTF32LE : UTF32 { static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); - return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + return (unsigned)c == 0x0000FEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - unsigned c = static_cast(is.Take()); - c |= static_cast(static_cast(is.Take())) << 8; - c |= static_cast(static_cast(is.Take())) << 16; - c |= static_cast(static_cast(is.Take())) << 24; - return static_cast(c); + CharType c = (unsigned char)is.Take(); + c |= (unsigned char)is.Take() << 8; + c |= (unsigned char)is.Take() << 16; + c |= (unsigned char)is.Take() << 24; + return c; } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast(0xFFu)); - os.Put(static_cast(0xFEu)); - os.Put(static_cast(0x00u)); - os.Put(static_cast(0x00u)); + os.Put(0xFFu); os.Put(0xFEu); os.Put(0x00u); os.Put(0x00u); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast(c & 0xFFu)); - os.Put(static_cast((c >> 8) & 0xFFu)); - os.Put(static_cast((c >> 16) & 0xFFu)); - os.Put(static_cast((c >> 24) & 0xFFu)); + os.Put(c & 0xFFu); + os.Put((c >> 8) & 0xFFu); + os.Put((c >> 16) & 0xFFu); + os.Put((c >> 24) & 0xFFu); } }; @@ -498,35 +442,32 @@ struct UTF32BE : UTF32 { static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); - return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + return (unsigned)c == 0x0000FEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - unsigned c = static_cast(static_cast(is.Take())) << 24; - c |= static_cast(static_cast(is.Take())) << 16; - c |= static_cast(static_cast(is.Take())) << 8; - c |= static_cast(static_cast(is.Take())); - return static_cast(c); + CharType c = (unsigned char)is.Take() << 24; + c |= (unsigned char)is.Take() << 16; + c |= (unsigned char)is.Take() << 8; + c |= (unsigned char)is.Take(); + return c; } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast(0x00u)); - os.Put(static_cast(0x00u)); - os.Put(static_cast(0xFEu)); - os.Put(static_cast(0xFFu)); + os.Put(0x00u); os.Put(0x00u); os.Put(0xFEu); os.Put(0xFFu); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast((c >> 24) & 0xFFu)); - os.Put(static_cast((c >> 16) & 0xFFu)); - os.Put(static_cast((c >> 8) & 0xFFu)); - os.Put(static_cast(c & 0xFFu)); + os.Put((c >> 24) & 0xFFu); + os.Put((c >> 16) & 0xFFu); + os.Put((c >> 8) & 0xFFu); + os.Put(c & 0xFFu); } }; @@ -550,37 +491,31 @@ struct ASCII { os.Put(static_cast(codepoint & 0xFF)); } - template - static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { - RAPIDJSON_ASSERT(codepoint <= 0x7F); - PutUnsafe(os, static_cast(codepoint & 0xFF)); - } - template static bool Decode(InputStream& is, unsigned* codepoint) { - uint8_t c = static_cast(is.Take()); + unsigned char c = static_cast(is.Take()); *codepoint = c; return c <= 0X7F; } template static bool Validate(InputStream& is, OutputStream& os) { - uint8_t c = static_cast(is.Take()); - os.Put(static_cast(c)); + unsigned char c = is.Take(); + os.Put(c); return c <= 0x7F; } template static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - uint8_t c = static_cast(Take(is)); - return static_cast(c); + Ch c = Take(is); + return c; } template static Ch Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - return static_cast(is.Take()); + return is.Take(); } template @@ -620,28 +555,21 @@ struct AutoUTF { #define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x template - static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) { typedef void (*EncodeFunc)(OutputStream&, unsigned); static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; (*f[os.GetType()])(os, codepoint); } - template - static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) { - typedef void (*EncodeFunc)(OutputStream&, unsigned); - static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; - (*f[os.GetType()])(os, codepoint); - } - template - static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { typedef bool (*DecodeFunc)(InputStream&, unsigned*); static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; return (*f[is.GetType()])(is, codepoint); } template - static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { typedef bool (*ValidateFunc)(InputStream&, OutputStream&); static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; return (*f[is.GetType()])(is, os); @@ -658,7 +586,7 @@ template struct Transcoder { //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. template - static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { + RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { unsigned codepoint; if (!SourceEncoding::Decode(is, &codepoint)) return false; @@ -666,50 +594,31 @@ struct Transcoder { return true; } - template - static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { - unsigned codepoint; - if (!SourceEncoding::Decode(is, &codepoint)) - return false; - TargetEncoding::EncodeUnsafe(os, codepoint); - return true; - } - //! Validate one Unicode codepoint from an encoded stream. template - static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { return Transcode(is, os); // Since source/target encoding is different, must transcode. } }; -// Forward declaration. -template -inline void PutUnsafe(Stream& stream, typename Stream::Ch c); - //! Specialization of Transcoder with same source and target encoding. template struct Transcoder { template - static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { + RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. return true; } template - static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { - PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. - return true; - } - - template - static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { return Encoding::Validate(is, os); // source/target encoding are the same } }; RAPIDJSON_NAMESPACE_END -#if defined(__GNUC__) || defined(_MSC_VER) +#if defined(__GNUC__) || defined(_MSV_VER) RAPIDJSON_DIAG_POP #endif diff --git a/contrib/rapidjson/include/rapidjson/error/en.h b/contrib/rapidjson/include/rapidjson/error/en.h index 2db838bff..d5f9caab8 100644 --- a/contrib/rapidjson/include/rapidjson/error/en.h +++ b/contrib/rapidjson/include/rapidjson/error/en.h @@ -12,17 +12,11 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. -#ifndef RAPIDJSON_ERROR_EN_H_ -#define RAPIDJSON_ERROR_EN_H_ +#ifndef RAPIDJSON_ERROR_EN_H__ +#define RAPIDJSON_ERROR_EN_H__ #include "error.h" -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(switch-enum) -RAPIDJSON_DIAG_OFF(covered-switch-default) -#endif - RAPIDJSON_NAMESPACE_BEGIN //! Maps error code of parsing into error message. @@ -38,7 +32,7 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); - case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values."); + case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not follow by other values."); case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); @@ -61,14 +55,11 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); - default: return RAPIDJSON_ERROR_STRING("Unknown error."); + default: + return RAPIDJSON_ERROR_STRING("Unknown error."); } } RAPIDJSON_NAMESPACE_END -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_ERROR_EN_H_ +#endif // RAPIDJSON_ERROR_EN_H__ diff --git a/contrib/rapidjson/include/rapidjson/error/error.h b/contrib/rapidjson/include/rapidjson/error/error.h index 9311d2f03..f9094fb95 100644 --- a/contrib/rapidjson/include/rapidjson/error/error.h +++ b/contrib/rapidjson/include/rapidjson/error/error.h @@ -12,16 +12,11 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. -#ifndef RAPIDJSON_ERROR_ERROR_H_ -#define RAPIDJSON_ERROR_ERROR_H_ +#ifndef RAPIDJSON_ERROR_ERROR_H__ +#define RAPIDJSON_ERROR_ERROR_H__ #include "../rapidjson.h" -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(padded) -#endif - /*! \file error.h */ /*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ @@ -104,9 +99,7 @@ enum ParseErrorCode { \see GenericReader::Parse, GenericDocument::Parse */ struct ParseResult { - //!! Unspecified boolean type - typedef bool (ParseResult::*BooleanType)() const; -public: + //! Default constructor, no error. ParseResult() : code_(kParseErrorNone), offset_(0) {} //! Constructor to set an error. @@ -117,8 +110,8 @@ public: //! Get the error offset, if \ref IsError(), 0 otherwise. size_t Offset() const { return offset_; } - //! Explicit conversion to \c bool, returns \c true, iff !\ref IsError(). - operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; } + //! Conversion to \c bool, returns \c true, iff !\ref IsError(). + operator bool() const { return !IsError(); } //! Whether the result is an error. bool IsError() const { return code_ != kParseErrorNone; } @@ -126,10 +119,6 @@ public: bool operator==(ParseErrorCode code) const { return code_ == code; } friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } - bool operator!=(const ParseResult& that) const { return !(*this == that); } - bool operator!=(ParseErrorCode code) const { return !(*this == code); } - friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return err != code; } - //! Reset error code. void Clear() { Set(kParseErrorNone); } //! Update error code and offset. @@ -154,8 +143,4 @@ typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); RAPIDJSON_NAMESPACE_END -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_ERROR_ERROR_H_ +#endif // RAPIDJSON_ERROR_ERROR_H__ diff --git a/contrib/rapidjson/include/rapidjson/filereadstream.h b/contrib/rapidjson/include/rapidjson/filereadstream.h index b56ea13b3..3913eb74b 100644 --- a/contrib/rapidjson/include/rapidjson/filereadstream.h +++ b/contrib/rapidjson/include/rapidjson/filereadstream.h @@ -15,16 +15,9 @@ #ifndef RAPIDJSON_FILEREADSTREAM_H_ #define RAPIDJSON_FILEREADSTREAM_H_ -#include "stream.h" +#include "rapidjson.h" #include -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(padded) -RAPIDJSON_DIAG_OFF(unreachable-code) -RAPIDJSON_DIAG_OFF(missing-noreturn) -#endif - RAPIDJSON_NAMESPACE_BEGIN //! File byte stream for input using fread(). @@ -92,8 +85,4 @@ private: RAPIDJSON_NAMESPACE_END -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - #endif // RAPIDJSON_FILESTREAM_H_ diff --git a/contrib/rapidjson/include/rapidjson/filewritestream.h b/contrib/rapidjson/include/rapidjson/filewritestream.h index 6378dd60e..dfb9cbd02 100644 --- a/contrib/rapidjson/include/rapidjson/filewritestream.h +++ b/contrib/rapidjson/include/rapidjson/filewritestream.h @@ -15,14 +15,9 @@ #ifndef RAPIDJSON_FILEWRITESTREAM_H_ #define RAPIDJSON_FILEWRITESTREAM_H_ -#include "stream.h" +#include "rapidjson.h" #include -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(unreachable-code) -#endif - RAPIDJSON_NAMESPACE_BEGIN //! Wrapper of C file stream for input using fread(). @@ -62,11 +57,7 @@ public: void Flush() { if (current_ != buffer_) { - size_t result = fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); - if (result < static_cast(current_ - buffer_)) { - // failure deliberately ignored at this time - // added to avoid warn_unused_result build errors - } + fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); current_ = buffer_; } } @@ -97,8 +88,4 @@ inline void PutN(FileWriteStream& stream, char c, size_t n) { RAPIDJSON_NAMESPACE_END -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - #endif // RAPIDJSON_FILESTREAM_H_ diff --git a/contrib/rapidjson/include/rapidjson/fwd.h b/contrib/rapidjson/include/rapidjson/fwd.h deleted file mode 100644 index e8104e841..000000000 --- a/contrib/rapidjson/include/rapidjson/fwd.h +++ /dev/null @@ -1,151 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_FWD_H_ -#define RAPIDJSON_FWD_H_ - -#include "rapidjson.h" - -RAPIDJSON_NAMESPACE_BEGIN - -// encodings.h - -template struct UTF8; -template struct UTF16; -template struct UTF16BE; -template struct UTF16LE; -template struct UTF32; -template struct UTF32BE; -template struct UTF32LE; -template struct ASCII; -template struct AutoUTF; - -template -struct Transcoder; - -// allocators.h - -class CrtAllocator; - -template -class MemoryPoolAllocator; - -// stream.h - -template -struct GenericStringStream; - -typedef GenericStringStream > StringStream; - -template -struct GenericInsituStringStream; - -typedef GenericInsituStringStream > InsituStringStream; - -// stringbuffer.h - -template -class GenericStringBuffer; - -typedef GenericStringBuffer, CrtAllocator> StringBuffer; - -// filereadstream.h - -class FileReadStream; - -// filewritestream.h - -class FileWriteStream; - -// memorybuffer.h - -template -struct GenericMemoryBuffer; - -typedef GenericMemoryBuffer MemoryBuffer; - -// memorystream.h - -struct MemoryStream; - -// reader.h - -template -struct BaseReaderHandler; - -template -class GenericReader; - -typedef GenericReader, UTF8, CrtAllocator> Reader; - -// writer.h - -template -class Writer; - -// prettywriter.h - -template -class PrettyWriter; - -// document.h - -template -struct GenericMember; - -template -class GenericMemberIterator; - -template -struct GenericStringRef; - -template -class GenericValue; - -typedef GenericValue, MemoryPoolAllocator > Value; - -template -class GenericDocument; - -typedef GenericDocument, MemoryPoolAllocator, CrtAllocator> Document; - -// pointer.h - -template -class GenericPointer; - -typedef GenericPointer Pointer; - -// schema.h - -template -class IGenericRemoteSchemaDocumentProvider; - -template -class GenericSchemaDocument; - -typedef GenericSchemaDocument SchemaDocument; -typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; - -template < - typename SchemaDocumentType, - typename OutputHandler, - typename StateAllocator> -class GenericSchemaValidator; - -typedef GenericSchemaValidator, void>, CrtAllocator> SchemaValidator; - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_RAPIDJSONFWD_H_ diff --git a/contrib/rapidjson/include/rapidjson/internal/biginteger.h b/contrib/rapidjson/include/rapidjson/internal/biginteger.h index 9d3e88c99..99a30acf6 100644 --- a/contrib/rapidjson/include/rapidjson/internal/biginteger.h +++ b/contrib/rapidjson/include/rapidjson/internal/biginteger.h @@ -19,7 +19,6 @@ #if defined(_MSC_VER) && defined(_M_AMD64) #include // for _umul128 -#pragma intrinsic(_umul128) #endif RAPIDJSON_NAMESPACE_BEGIN @@ -51,16 +50,7 @@ public: if (length > 0) AppendDecimal64(decimals + i, decimals + i + length); } - - BigInteger& operator=(const BigInteger &rhs) - { - if (this != &rhs) { - count_ = rhs.count_; - std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); - } - return *this; - } - + BigInteger& operator=(uint64_t u) { digits_[0] = u; count_ = 1; @@ -240,7 +230,7 @@ private: uint64_t r = 0; for (const char* p = begin; p != end; ++p) { RAPIDJSON_ASSERT(*p >= '0' && *p <= '9'); - r = r * 10u + static_cast(*p - '0'); + r = r * 10 + (*p - '0'); } return r; } diff --git a/contrib/rapidjson/include/rapidjson/internal/diyfp.h b/contrib/rapidjson/include/rapidjson/internal/diyfp.h index 29abf8046..3b6c4238c 100644 --- a/contrib/rapidjson/include/rapidjson/internal/diyfp.h +++ b/contrib/rapidjson/include/rapidjson/internal/diyfp.h @@ -21,10 +21,9 @@ #include "../rapidjson.h" -#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER) +#if defined(_MSC_VER) && defined(_M_AMD64) #include #pragma intrinsic(_BitScanReverse64) -#pragma intrinsic(_umul128) #endif RAPIDJSON_NAMESPACE_BEGIN @@ -35,13 +34,8 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(padded) -#endif - struct DiyFp { - DiyFp() : f(), e() {} + DiyFp() {} DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} @@ -238,8 +232,8 @@ inline DiyFp GetCachedPower(int e, int* K) { } inline DiyFp GetCachedPower10(int exp, int *outExp) { - unsigned index = (static_cast(exp) + 348u) / 8u; - *outExp = -348 + static_cast(index) * 8; + unsigned index = (exp + 348) / 8; + *outExp = -348 + index * 8; return GetCachedPowerByIndex(index); } @@ -247,11 +241,6 @@ inline DiyFp GetCachedPower10(int exp, int *outExp) { RAPIDJSON_DIAG_POP #endif -#ifdef __clang__ -RAPIDJSON_DIAG_POP -RAPIDJSON_DIAG_OFF(padded) -#endif - } // namespace internal RAPIDJSON_NAMESPACE_END diff --git a/contrib/rapidjson/include/rapidjson/internal/dtoa.h b/contrib/rapidjson/include/rapidjson/internal/dtoa.h index bf2e9b2e5..2d8d2e46a 100644 --- a/contrib/rapidjson/include/rapidjson/internal/dtoa.h +++ b/contrib/rapidjson/include/rapidjson/internal/dtoa.h @@ -29,7 +29,6 @@ namespace internal { #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) -RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124 #endif inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { @@ -41,7 +40,7 @@ inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uin } } -inline int CountDecimalDigit32(uint32_t n) { +inline unsigned CountDecimalDigit32(uint32_t n) { // Simple pure C++ implementation was faster than __builtin_clz version in this situation. if (n < 10) return 1; if (n < 100) return 2; @@ -102,8 +101,7 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff kappa--; if (p2 < delta) { *K += kappa; - int index = -kappa; - GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[index] : 0)); + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-kappa]); return; } } @@ -147,10 +145,10 @@ inline char* WriteExponent(int K, char* buffer) { return buffer; } -inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { +inline char* Prettify(char* buffer, int length, int k) { const int kk = length + k; // 10^(kk-1) <= v < 10^kk - if (0 <= k && kk <= 21) { + if (length <= kk && kk <= 21) { // 1234e7 -> 12340000000 for (int i = length; i < kk; i++) buffer[i] = '0'; @@ -160,44 +158,19 @@ inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { } else if (0 < kk && kk <= 21) { // 1234e-2 -> 12.34 - std::memmove(&buffer[kk + 1], &buffer[kk], static_cast(length - kk)); + std::memmove(&buffer[kk + 1], &buffer[kk], length - kk); buffer[kk] = '.'; - if (0 > k + maxDecimalPlaces) { - // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1 - // Remove extra trailing zeros (at least one) after truncation. - for (int i = kk + maxDecimalPlaces; i > kk + 1; i--) - if (buffer[i] != '0') - return &buffer[i + 1]; - return &buffer[kk + 2]; // Reserve one zero - } - else - return &buffer[length + 1]; + return &buffer[length + 1]; } else if (-6 < kk && kk <= 0) { // 1234e-6 -> 0.001234 const int offset = 2 - kk; - std::memmove(&buffer[offset], &buffer[0], static_cast(length)); + std::memmove(&buffer[offset], &buffer[0], length); buffer[0] = '0'; buffer[1] = '.'; for (int i = 2; i < offset; i++) buffer[i] = '0'; - if (length - kk > maxDecimalPlaces) { - // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1 - // Remove extra trailing zeros (at least one) after truncation. - for (int i = maxDecimalPlaces + 1; i > 2; i--) - if (buffer[i] != '0') - return &buffer[i + 1]; - return &buffer[3]; // Reserve one zero - } - else - return &buffer[length + offset]; - } - else if (kk < -maxDecimalPlaces) { - // Truncate to zero - buffer[0] = '0'; - buffer[1] = '.'; - buffer[2] = '0'; - return &buffer[3]; + return &buffer[length + offset]; } else if (length == 1) { // 1e30 @@ -206,15 +179,14 @@ inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { } else { // 1234e30 -> 1.234e33 - std::memmove(&buffer[2], &buffer[1], static_cast(length - 1)); + std::memmove(&buffer[2], &buffer[1], length - 1); buffer[1] = '.'; buffer[length + 1] = 'e'; return WriteExponent(kk - 1, &buffer[0 + length + 2]); } } -inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) { - RAPIDJSON_ASSERT(maxDecimalPlaces >= 1); +inline char* dtoa(double value, char* buffer) { Double d(value); if (d.IsZero()) { if (d.Sign()) @@ -231,7 +203,7 @@ inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) { } int length, K; Grisu2(value, buffer, &length, &K); - return Prettify(buffer, length, K, maxDecimalPlaces); + return Prettify(buffer, length, K); } } diff --git a/contrib/rapidjson/include/rapidjson/internal/ieee754.h b/contrib/rapidjson/include/rapidjson/internal/ieee754.h index c2684ba2a..e3f03364c 100644 --- a/contrib/rapidjson/include/rapidjson/internal/ieee754.h +++ b/contrib/rapidjson/include/rapidjson/internal/ieee754.h @@ -40,7 +40,6 @@ public: bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; } bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; } - bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; } bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; } bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; } @@ -48,7 +47,7 @@ public: int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } - static int EffectiveSignificandSize(int order) { + static unsigned EffectiveSignificandSize(int order) { if (order >= -1021) return 53; else if (order <= -1074) diff --git a/contrib/rapidjson/include/rapidjson/internal/regex.h b/contrib/rapidjson/include/rapidjson/internal/regex.h deleted file mode 100644 index e1a2faae5..000000000 --- a/contrib/rapidjson/include/rapidjson/internal/regex.h +++ /dev/null @@ -1,734 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_INTERNAL_REGEX_H_ -#define RAPIDJSON_INTERNAL_REGEX_H_ - -#include "../allocators.h" -#include "../stream.h" -#include "stack.h" - -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(padded) -RAPIDJSON_DIAG_OFF(switch-enum) -RAPIDJSON_DIAG_OFF(implicit-fallthrough) -#endif - -#ifdef __GNUC__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(effc++) -#if __GNUC__ >= 7 -RAPIDJSON_DIAG_OFF(implicit-fallthrough) -#endif -#endif - -#ifdef _MSC_VER -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated -#endif - -#ifndef RAPIDJSON_REGEX_VERBOSE -#define RAPIDJSON_REGEX_VERBOSE 0 -#endif - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -/////////////////////////////////////////////////////////////////////////////// -// DecodedStream - -template -class DecodedStream { -public: - DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } - unsigned Peek() { return codepoint_; } - unsigned Take() { - unsigned c = codepoint_; - if (c) // No further decoding when '\0' - Decode(); - return c; - } - -private: - void Decode() { - if (!Encoding::Decode(ss_, &codepoint_)) - codepoint_ = 0; - } - - SourceStream& ss_; - unsigned codepoint_; -}; - -/////////////////////////////////////////////////////////////////////////////// -// GenericRegex - -static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 -static const SizeType kRegexInvalidRange = ~SizeType(0); - -template -class GenericRegexSearch; - -//! Regular expression engine with subset of ECMAscript grammar. -/*! - Supported regular expression syntax: - - \c ab Concatenation - - \c a|b Alternation - - \c a? Zero or one - - \c a* Zero or more - - \c a+ One or more - - \c a{3} Exactly 3 times - - \c a{3,} At least 3 times - - \c a{3,5} 3 to 5 times - - \c (ab) Grouping - - \c ^a At the beginning - - \c a$ At the end - - \c . Any character - - \c [abc] Character classes - - \c [a-c] Character class range - - \c [a-z0-9_] Character class combination - - \c [^abc] Negated character classes - - \c [^a-c] Negated character class range - - \c [\b] Backspace (U+0008) - - \c \\| \\\\ ... Escape characters - - \c \\f Form feed (U+000C) - - \c \\n Line feed (U+000A) - - \c \\r Carriage return (U+000D) - - \c \\t Tab (U+0009) - - \c \\v Vertical tab (U+000B) - - \note This is a Thompson NFA engine, implemented with reference to - Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).", - https://swtch.com/~rsc/regexp/regexp1.html -*/ -template -class GenericRegex { -public: - typedef Encoding EncodingType; - typedef typename Encoding::Ch Ch; - template friend class GenericRegexSearch; - - GenericRegex(const Ch* source, Allocator* allocator = 0) : - states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), - anchorBegin_(), anchorEnd_() - { - GenericStringStream ss(source); - DecodedStream, Encoding> ds(ss); - Parse(ds); - } - - ~GenericRegex() {} - - bool IsValid() const { - return root_ != kRegexInvalidState; - } - -private: - enum Operator { - kZeroOrOne, - kZeroOrMore, - kOneOrMore, - kConcatenation, - kAlternation, - kLeftParenthesis - }; - - static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.' - static const unsigned kRangeCharacterClass = 0xFFFFFFFE; - static const unsigned kRangeNegationFlag = 0x80000000; - - struct Range { - unsigned start; // - unsigned end; - SizeType next; - }; - - struct State { - SizeType out; //!< Equals to kInvalid for matching state - SizeType out1; //!< Equals to non-kInvalid for split - SizeType rangeStart; - unsigned codepoint; - }; - - struct Frag { - Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {} - SizeType start; - SizeType out; //!< link-list of all output states - SizeType minIndex; - }; - - State& GetState(SizeType index) { - RAPIDJSON_ASSERT(index < stateCount_); - return states_.template Bottom()[index]; - } - - const State& GetState(SizeType index) const { - RAPIDJSON_ASSERT(index < stateCount_); - return states_.template Bottom()[index]; - } - - Range& GetRange(SizeType index) { - RAPIDJSON_ASSERT(index < rangeCount_); - return ranges_.template Bottom()[index]; - } - - const Range& GetRange(SizeType index) const { - RAPIDJSON_ASSERT(index < rangeCount_); - return ranges_.template Bottom()[index]; - } - - template - void Parse(DecodedStream& ds) { - Allocator allocator; - Stack operandStack(&allocator, 256); // Frag - Stack operatorStack(&allocator, 256); // Operator - Stack atomCountStack(&allocator, 256); // unsigned (Atom per parenthesis) - - *atomCountStack.template Push() = 0; - - unsigned codepoint; - while (ds.Peek() != 0) { - switch (codepoint = ds.Take()) { - case '^': - anchorBegin_ = true; - break; - - case '$': - anchorEnd_ = true; - break; - - case '|': - while (!operatorStack.Empty() && *operatorStack.template Top() < kAlternation) - if (!Eval(operandStack, *operatorStack.template Pop(1))) - return; - *operatorStack.template Push() = kAlternation; - *atomCountStack.template Top() = 0; - break; - - case '(': - *operatorStack.template Push() = kLeftParenthesis; - *atomCountStack.template Push() = 0; - break; - - case ')': - while (!operatorStack.Empty() && *operatorStack.template Top() != kLeftParenthesis) - if (!Eval(operandStack, *operatorStack.template Pop(1))) - return; - if (operatorStack.Empty()) - return; - operatorStack.template Pop(1); - atomCountStack.template Pop(1); - ImplicitConcatenation(atomCountStack, operatorStack); - break; - - case '?': - if (!Eval(operandStack, kZeroOrOne)) - return; - break; - - case '*': - if (!Eval(operandStack, kZeroOrMore)) - return; - break; - - case '+': - if (!Eval(operandStack, kOneOrMore)) - return; - break; - - case '{': - { - unsigned n, m; - if (!ParseUnsigned(ds, &n)) - return; - - if (ds.Peek() == ',') { - ds.Take(); - if (ds.Peek() == '}') - m = kInfinityQuantifier; - else if (!ParseUnsigned(ds, &m) || m < n) - return; - } - else - m = n; - - if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}') - return; - ds.Take(); - } - break; - - case '.': - PushOperand(operandStack, kAnyCharacterClass); - ImplicitConcatenation(atomCountStack, operatorStack); - break; - - case '[': - { - SizeType range; - if (!ParseRange(ds, &range)) - return; - SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass); - GetState(s).rangeStart = range; - *operandStack.template Push() = Frag(s, s, s); - } - ImplicitConcatenation(atomCountStack, operatorStack); - break; - - case '\\': // Escape character - if (!CharacterEscape(ds, &codepoint)) - return; // Unsupported escape character - // fall through to default - - default: // Pattern character - PushOperand(operandStack, codepoint); - ImplicitConcatenation(atomCountStack, operatorStack); - } - } - - while (!operatorStack.Empty()) - if (!Eval(operandStack, *operatorStack.template Pop(1))) - return; - - // Link the operand to matching state. - if (operandStack.GetSize() == sizeof(Frag)) { - Frag* e = operandStack.template Pop(1); - Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0)); - root_ = e->start; - -#if RAPIDJSON_REGEX_VERBOSE - printf("root: %d\n", root_); - for (SizeType i = 0; i < stateCount_ ; i++) { - State& s = GetState(i); - printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint); - } - printf("\n"); -#endif - } - } - - SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { - State* s = states_.template Push(); - s->out = out; - s->out1 = out1; - s->codepoint = codepoint; - s->rangeStart = kRegexInvalidRange; - return stateCount_++; - } - - void PushOperand(Stack& operandStack, unsigned codepoint) { - SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint); - *operandStack.template Push() = Frag(s, s, s); - } - - void ImplicitConcatenation(Stack& atomCountStack, Stack& operatorStack) { - if (*atomCountStack.template Top()) - *operatorStack.template Push() = kConcatenation; - (*atomCountStack.template Top())++; - } - - SizeType Append(SizeType l1, SizeType l2) { - SizeType old = l1; - while (GetState(l1).out != kRegexInvalidState) - l1 = GetState(l1).out; - GetState(l1).out = l2; - return old; - } - - void Patch(SizeType l, SizeType s) { - for (SizeType next; l != kRegexInvalidState; l = next) { - next = GetState(l).out; - GetState(l).out = s; - } - } - - bool Eval(Stack& operandStack, Operator op) { - switch (op) { - case kConcatenation: - RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2); - { - Frag e2 = *operandStack.template Pop(1); - Frag e1 = *operandStack.template Pop(1); - Patch(e1.out, e2.start); - *operandStack.template Push() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex)); - } - return true; - - case kAlternation: - if (operandStack.GetSize() >= sizeof(Frag) * 2) { - Frag e2 = *operandStack.template Pop(1); - Frag e1 = *operandStack.template Pop(1); - SizeType s = NewState(e1.start, e2.start, 0); - *operandStack.template Push() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex)); - return true; - } - return false; - - case kZeroOrOne: - if (operandStack.GetSize() >= sizeof(Frag)) { - Frag e = *operandStack.template Pop(1); - SizeType s = NewState(kRegexInvalidState, e.start, 0); - *operandStack.template Push() = Frag(s, Append(e.out, s), e.minIndex); - return true; - } - return false; - - case kZeroOrMore: - if (operandStack.GetSize() >= sizeof(Frag)) { - Frag e = *operandStack.template Pop(1); - SizeType s = NewState(kRegexInvalidState, e.start, 0); - Patch(e.out, s); - *operandStack.template Push() = Frag(s, s, e.minIndex); - return true; - } - return false; - - default: - RAPIDJSON_ASSERT(op == kOneOrMore); - if (operandStack.GetSize() >= sizeof(Frag)) { - Frag e = *operandStack.template Pop(1); - SizeType s = NewState(kRegexInvalidState, e.start, 0); - Patch(e.out, s); - *operandStack.template Push() = Frag(e.start, s, e.minIndex); - return true; - } - return false; - } - } - - bool EvalQuantifier(Stack& operandStack, unsigned n, unsigned m) { - RAPIDJSON_ASSERT(n <= m); - RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag)); - - if (n == 0) { - if (m == 0) // a{0} not support - return false; - else if (m == kInfinityQuantifier) - Eval(operandStack, kZeroOrMore); // a{0,} -> a* - else { - Eval(operandStack, kZeroOrOne); // a{0,5} -> a? - for (unsigned i = 0; i < m - 1; i++) - CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a? - for (unsigned i = 0; i < m - 1; i++) - Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a? - } - return true; - } - - for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a - CloneTopOperand(operandStack); - - if (m == kInfinityQuantifier) - Eval(operandStack, kOneOrMore); // a{3,} -> a a a+ - else if (m > n) { - CloneTopOperand(operandStack); // a{3,5} -> a a a a - Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a? - for (unsigned i = n; i < m - 1; i++) - CloneTopOperand(operandStack); // a{3,5} -> a a a a? a? - for (unsigned i = n; i < m; i++) - Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a? - } - - for (unsigned i = 0; i < n - 1; i++) - Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a? - - return true; - } - - static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; } - - void CloneTopOperand(Stack& operandStack) { - const Frag src = *operandStack.template Top(); // Copy constructor to prevent invalidation - SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_) - State* s = states_.template Push(count); - memcpy(s, &GetState(src.minIndex), count * sizeof(State)); - for (SizeType j = 0; j < count; j++) { - if (s[j].out != kRegexInvalidState) - s[j].out += count; - if (s[j].out1 != kRegexInvalidState) - s[j].out1 += count; - } - *operandStack.template Push() = Frag(src.start + count, src.out + count, src.minIndex + count); - stateCount_ += count; - } - - template - bool ParseUnsigned(DecodedStream& ds, unsigned* u) { - unsigned r = 0; - if (ds.Peek() < '0' || ds.Peek() > '9') - return false; - while (ds.Peek() >= '0' && ds.Peek() <= '9') { - if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295 - return false; // overflow - r = r * 10 + (ds.Take() - '0'); - } - *u = r; - return true; - } - - template - bool ParseRange(DecodedStream& ds, SizeType* range) { - bool isBegin = true; - bool negate = false; - int step = 0; - SizeType start = kRegexInvalidRange; - SizeType current = kRegexInvalidRange; - unsigned codepoint; - while ((codepoint = ds.Take()) != 0) { - if (isBegin) { - isBegin = false; - if (codepoint == '^') { - negate = true; - continue; - } - } - - switch (codepoint) { - case ']': - if (start == kRegexInvalidRange) - return false; // Error: nothing inside [] - if (step == 2) { // Add trailing '-' - SizeType r = NewRange('-'); - RAPIDJSON_ASSERT(current != kRegexInvalidRange); - GetRange(current).next = r; - } - if (negate) - GetRange(start).start |= kRangeNegationFlag; - *range = start; - return true; - - case '\\': - if (ds.Peek() == 'b') { - ds.Take(); - codepoint = 0x0008; // Escape backspace character - } - else if (!CharacterEscape(ds, &codepoint)) - return false; - // fall through to default - - default: - switch (step) { - case 1: - if (codepoint == '-') { - step++; - break; - } - // fall through to step 0 for other characters - - case 0: - { - SizeType r = NewRange(codepoint); - if (current != kRegexInvalidRange) - GetRange(current).next = r; - if (start == kRegexInvalidRange) - start = r; - current = r; - } - step = 1; - break; - - default: - RAPIDJSON_ASSERT(step == 2); - GetRange(current).end = codepoint; - step = 0; - } - } - } - return false; - } - - SizeType NewRange(unsigned codepoint) { - Range* r = ranges_.template Push(); - r->start = r->end = codepoint; - r->next = kRegexInvalidRange; - return rangeCount_++; - } - - template - bool CharacterEscape(DecodedStream& ds, unsigned* escapedCodepoint) { - unsigned codepoint; - switch (codepoint = ds.Take()) { - case '^': - case '$': - case '|': - case '(': - case ')': - case '?': - case '*': - case '+': - case '.': - case '[': - case ']': - case '{': - case '}': - case '\\': - *escapedCodepoint = codepoint; return true; - case 'f': *escapedCodepoint = 0x000C; return true; - case 'n': *escapedCodepoint = 0x000A; return true; - case 'r': *escapedCodepoint = 0x000D; return true; - case 't': *escapedCodepoint = 0x0009; return true; - case 'v': *escapedCodepoint = 0x000B; return true; - default: - return false; // Unsupported escape character - } - } - - Stack states_; - Stack ranges_; - SizeType root_; - SizeType stateCount_; - SizeType rangeCount_; - - static const unsigned kInfinityQuantifier = ~0u; - - // For SearchWithAnchoring() - bool anchorBegin_; - bool anchorEnd_; -}; - -template -class GenericRegexSearch { -public: - typedef typename RegexType::EncodingType Encoding; - typedef typename Encoding::Ch Ch; - - GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) : - regex_(regex), allocator_(allocator), ownAllocator_(0), - state0_(allocator, 0), state1_(allocator, 0), stateSet_() - { - RAPIDJSON_ASSERT(regex_.IsValid()); - if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); - stateSet_ = static_cast(allocator_->Malloc(GetStateSetSize())); - state0_.template Reserve(regex_.stateCount_); - state1_.template Reserve(regex_.stateCount_); - } - - ~GenericRegexSearch() { - Allocator::Free(stateSet_); - RAPIDJSON_DELETE(ownAllocator_); - } - - template - bool Match(InputStream& is) { - return SearchWithAnchoring(is, true, true); - } - - bool Match(const Ch* s) { - GenericStringStream is(s); - return Match(is); - } - - template - bool Search(InputStream& is) { - return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_); - } - - bool Search(const Ch* s) { - GenericStringStream is(s); - return Search(is); - } - -private: - typedef typename RegexType::State State; - typedef typename RegexType::Range Range; - - template - bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) { - DecodedStream ds(is); - - state0_.Clear(); - Stack *current = &state0_, *next = &state1_; - const size_t stateSetSize = GetStateSetSize(); - std::memset(stateSet_, 0, stateSetSize); - - bool matched = AddState(*current, regex_.root_); - unsigned codepoint; - while (!current->Empty() && (codepoint = ds.Take()) != 0) { - std::memset(stateSet_, 0, stateSetSize); - next->Clear(); - matched = false; - for (const SizeType* s = current->template Bottom(); s != current->template End(); ++s) { - const State& sr = regex_.GetState(*s); - if (sr.codepoint == codepoint || - sr.codepoint == RegexType::kAnyCharacterClass || - (sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) - { - matched = AddState(*next, sr.out) || matched; - if (!anchorEnd && matched) - return true; - } - if (!anchorBegin) - AddState(*next, regex_.root_); - } - internal::Swap(current, next); - } - - return matched; - } - - size_t GetStateSetSize() const { - return (regex_.stateCount_ + 31) / 32 * 4; - } - - // Return whether the added states is a match state - bool AddState(Stack& l, SizeType index) { - RAPIDJSON_ASSERT(index != kRegexInvalidState); - - const State& s = regex_.GetState(index); - if (s.out1 != kRegexInvalidState) { // Split - bool matched = AddState(l, s.out); - return AddState(l, s.out1) || matched; - } - else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) { - stateSet_[index >> 5] |= (1u << (index & 31)); - *l.template PushUnsafe() = index; - } - return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. - } - - bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { - bool yes = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0; - while (rangeIndex != kRegexInvalidRange) { - const Range& r = regex_.GetRange(rangeIndex); - if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end) - return yes; - rangeIndex = r.next; - } - return !yes; - } - - const RegexType& regex_; - Allocator* allocator_; - Allocator* ownAllocator_; - Stack state0_; - Stack state1_; - uint32_t* stateSet_; -}; - -typedef GenericRegex > Regex; -typedef GenericRegexSearch RegexSearch; - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - -#ifdef _MSC_VER -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_INTERNAL_REGEX_H_ diff --git a/contrib/rapidjson/include/rapidjson/internal/stack.h b/contrib/rapidjson/include/rapidjson/internal/stack.h index 5c5398c35..722d56923 100644 --- a/contrib/rapidjson/include/rapidjson/internal/stack.h +++ b/contrib/rapidjson/include/rapidjson/internal/stack.h @@ -15,13 +15,7 @@ #ifndef RAPIDJSON_INTERNAL_STACK_H_ #define RAPIDJSON_INTERNAL_STACK_H_ -#include "../allocators.h" -#include "swap.h" - -#if defined(__clang__) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(c++98-compat) -#endif +#include "../rapidjson.h" RAPIDJSON_NAMESPACE_BEGIN namespace internal { @@ -38,6 +32,7 @@ public: // Optimization note: Do not allocate memory for stack_ in constructor. // Do it lazily when first Push() -> Expand() -> Resize(). Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { + RAPIDJSON_ASSERT(stackCapacity > 0); } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS @@ -86,15 +81,6 @@ public: } #endif - void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT { - internal::Swap(allocator_, rhs.allocator_); - internal::Swap(ownAllocator_, rhs.ownAllocator_); - internal::Swap(stack_, rhs.stack_); - internal::Swap(stackTop_, rhs.stackTop_); - internal::Swap(stackEnd_, rhs.stackEnd_); - internal::Swap(initialCapacity_, rhs.initialCapacity_); - } - void Clear() { stackTop_ = stack_; } void ShrinkToFit() { @@ -111,23 +97,12 @@ public: // Optimization note: try to minimize the size of this function for force inline. // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. - template - RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { - // Expand the stack if needed - if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_)) - Expand(count); - } - template RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { - Reserve(count); - return PushUnsafe(count); - } + // Expand the stack if needed + if (stackTop_ + sizeof(T) * count >= stackEnd_) + Expand(count); - template - RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { - RAPIDJSON_ASSERT(stackTop_); - RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_); T* ret = reinterpret_cast(stackTop_); stackTop_ += sizeof(T) * count; return ret; @@ -147,32 +122,9 @@ public: } template - const T* Top() const { - RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); - return reinterpret_cast(stackTop_ - sizeof(T)); - } - - template - T* End() { return reinterpret_cast(stackTop_); } - - template - const T* End() const { return reinterpret_cast(stackTop_); } - - template - T* Bottom() { return reinterpret_cast(stack_); } - - template - const T* Bottom() const { return reinterpret_cast(stack_); } - - bool HasAllocator() const { - return allocator_ != 0; - } - - Allocator& GetAllocator() { - RAPIDJSON_ASSERT(allocator_); - return *allocator_; - } + T* Bottom() { return (T*)stack_; } + Allocator& GetAllocator() { return *allocator_; } bool Empty() const { return stackTop_ == stack_; } size_t GetSize() const { return static_cast(stackTop_ - stack_); } size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } @@ -184,7 +136,7 @@ private: size_t newCapacity; if (stack_ == 0) { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); newCapacity = initialCapacity_; } else { newCapacity = GetCapacity(); @@ -199,7 +151,7 @@ private: void Resize(size_t newCapacity) { const size_t size = GetSize(); // Backup the current size - stack_ = static_cast(allocator_->Realloc(stack_, GetCapacity(), newCapacity)); + stack_ = (char*)allocator_->Realloc(stack_, GetCapacity(), newCapacity); stackTop_ = stack_ + size; stackEnd_ = stack_ + newCapacity; } @@ -224,8 +176,4 @@ private: } // namespace internal RAPIDJSON_NAMESPACE_END -#if defined(__clang__) -RAPIDJSON_DIAG_POP -#endif - #endif // RAPIDJSON_STACK_H_ diff --git a/contrib/rapidjson/include/rapidjson/internal/strfunc.h b/contrib/rapidjson/include/rapidjson/internal/strfunc.h index 226439a76..84405065a 100644 --- a/contrib/rapidjson/include/rapidjson/internal/strfunc.h +++ b/contrib/rapidjson/include/rapidjson/internal/strfunc.h @@ -15,8 +15,7 @@ #ifndef RAPIDJSON_INTERNAL_STRFUNC_H_ #define RAPIDJSON_INTERNAL_STRFUNC_H_ -#include "../stream.h" -#include +#include "../rapidjson.h" RAPIDJSON_NAMESPACE_BEGIN namespace internal { @@ -29,40 +28,11 @@ namespace internal { */ template inline SizeType StrLen(const Ch* s) { - RAPIDJSON_ASSERT(s != 0); const Ch* p = s; while (*p) ++p; return SizeType(p - s); } -template <> -inline SizeType StrLen(const char* s) { - return SizeType(std::strlen(s)); -} - -template <> -inline SizeType StrLen(const wchar_t* s) { - return SizeType(std::wcslen(s)); -} - -//! Returns number of code points in a encoded string. -template -bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { - RAPIDJSON_ASSERT(s != 0); - RAPIDJSON_ASSERT(outCount != 0); - GenericStringStream is(s); - const typename Encoding::Ch* end = s + length; - SizeType count = 0; - while (is.src_ < end) { - unsigned codepoint; - if (!Encoding::Decode(is, &codepoint)) - return false; - count++; - } - *outCount = count; - return true; -} - } // namespace internal RAPIDJSON_NAMESPACE_END diff --git a/contrib/rapidjson/include/rapidjson/internal/strtod.h b/contrib/rapidjson/include/rapidjson/internal/strtod.h index adf49e349..ace65f677 100644 --- a/contrib/rapidjson/include/rapidjson/internal/strtod.h +++ b/contrib/rapidjson/include/rapidjson/internal/strtod.h @@ -15,6 +15,7 @@ #ifndef RAPIDJSON_STRTOD_ #define RAPIDJSON_STRTOD_ +#include "../rapidjson.h" #include "ieee754.h" #include "biginteger.h" #include "diyfp.h" @@ -94,13 +95,13 @@ inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { hS_Exp2 -= common_Exp2; BigInteger dS = d; - dS.MultiplyPow5(static_cast(dS_Exp5)) <<= static_cast(dS_Exp2); + dS.MultiplyPow5(dS_Exp5) <<= dS_Exp2; BigInteger bS(bInt); - bS.MultiplyPow5(static_cast(bS_Exp5)) <<= static_cast(bS_Exp2); + bS.MultiplyPow5(bS_Exp5) <<= bS_Exp2; BigInteger hS(1); - hS.MultiplyPow5(static_cast(hS_Exp5)) <<= static_cast(hS_Exp2); + hS.MultiplyPow5(hS_Exp5) <<= hS_Exp2; BigInteger delta(0); dS.Difference(bS, &delta); @@ -133,22 +134,22 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) break; - significand = significand * 10u + static_cast(decimals[i] - '0'); + significand = significand * 10 + (decimals[i] - '0'); } if (i < length && decimals[i] >= '5') // Rounding significand++; size_t remaining = length - i; - const int kUlpShift = 3; - const int kUlp = 1 << kUlpShift; - int64_t error = (remaining == 0) ? 0 : kUlp / 2; + const unsigned kUlpShift = 3; + const unsigned kUlp = 1 << kUlpShift; + int error = (remaining == 0) ? 0 : kUlp / 2; DiyFp v(significand, 0); v = v.Normalize(); error <<= -v.e; - const int dExp = static_cast(decimalPosition) - static_cast(i) + exp; + const int dExp = (int)decimalPosition - (int)i + exp; int actualExp; DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); @@ -162,10 +163,10 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6 DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7 }; - int adjustment = dExp - actualExp - 1; + int adjustment = dExp - actualExp - 1; RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7); v = v * kPow10[adjustment]; - if (length + static_cast(adjustment)> 19u) // has more digits than decimal digits in 64-bit + if (length + adjustment > 19) // has more digits than decimal digits in 64-bit error += kUlp / 2; } @@ -177,10 +178,10 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit v = v.Normalize(); error <<= oldExp - v.e; - const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); - int precisionSize = 64 - effectiveSignificandSize; + const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + unsigned precisionSize = 64 - effectiveSignificandSize; if (precisionSize + kUlpShift >= 64) { - int scaleExp = (precisionSize + kUlpShift) - 63; + unsigned scaleExp = (precisionSize + kUlpShift) - 63; v.f >>= scaleExp; v.e += scaleExp; error = (error >> scaleExp) + 1 + kUlp; @@ -190,7 +191,7 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; - if (precisionBits >= halfWay + static_cast(error)) { + if (precisionBits >= halfWay + error) { rounded.f++; if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340) rounded.f >>= 1; @@ -200,12 +201,12 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit *result = rounded.ToDouble(); - return halfWay - static_cast(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); + return halfWay - error >= precisionBits || precisionBits >= halfWay + error; } inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) { const BigInteger dInt(decimals, length); - const int dExp = static_cast(decimalPosition) - static_cast(length) + exp; + const int dExp = (int)decimalPosition - (int)length + exp; Double a(approx); int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); if (cmp < 0) @@ -245,10 +246,10 @@ inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t // Trim right-most digits const int kMaxDecimalDigit = 780; - if (static_cast(length) > kMaxDecimalDigit) { - int delta = (static_cast(length) - kMaxDecimalDigit); + if ((int)length > kMaxDecimalDigit) { + int delta = (int(length) - kMaxDecimalDigit); exp += delta; - decimalPosition -= static_cast(delta); + decimalPosition -= delta; length = kMaxDecimalDigit; } diff --git a/contrib/rapidjson/include/rapidjson/internal/swap.h b/contrib/rapidjson/include/rapidjson/internal/swap.h index 666e49f97..0590921f1 100644 --- a/contrib/rapidjson/include/rapidjson/internal/swap.h +++ b/contrib/rapidjson/include/rapidjson/internal/swap.h @@ -17,11 +17,6 @@ #include "../rapidjson.h" -#if defined(__clang__) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(c++98-compat) -#endif - RAPIDJSON_NAMESPACE_BEGIN namespace internal { @@ -39,8 +34,4 @@ inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT { } // namespace internal RAPIDJSON_NAMESPACE_END -#if defined(__clang__) -RAPIDJSON_DIAG_POP -#endif - #endif // RAPIDJSON_INTERNAL_SWAP_H_ diff --git a/contrib/rapidjson/include/rapidjson/istreamwrapper.h b/contrib/rapidjson/include/rapidjson/istreamwrapper.h deleted file mode 100644 index 8639c8c3c..000000000 --- a/contrib/rapidjson/include/rapidjson/istreamwrapper.h +++ /dev/null @@ -1,115 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_ISTREAMWRAPPER_H_ -#define RAPIDJSON_ISTREAMWRAPPER_H_ - -#include "stream.h" -#include - -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(padded) -#endif - -#ifdef _MSC_VER -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept. -/*! - The classes can be wrapped including but not limited to: - - - \c std::istringstream - - \c std::stringstream - - \c std::wistringstream - - \c std::wstringstream - - \c std::ifstream - - \c std::fstream - - \c std::wifstream - - \c std::wfstream - - \tparam StreamType Class derived from \c std::basic_istream. -*/ - -template -class BasicIStreamWrapper { -public: - typedef typename StreamType::char_type Ch; - BasicIStreamWrapper(StreamType& stream) : stream_(stream), count_(), peekBuffer_() {} - - Ch Peek() const { - typename StreamType::int_type c = stream_.peek(); - return RAPIDJSON_LIKELY(c != StreamType::traits_type::eof()) ? static_cast(c) : static_cast('\0'); - } - - Ch Take() { - typename StreamType::int_type c = stream_.get(); - if (RAPIDJSON_LIKELY(c != StreamType::traits_type::eof())) { - count_++; - return static_cast(c); - } - else - return '\0'; - } - - // tellg() may return -1 when failed. So we count by ourself. - size_t Tell() const { return count_; } - - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - void Put(Ch) { RAPIDJSON_ASSERT(false); } - void Flush() { RAPIDJSON_ASSERT(false); } - size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } - - // For encoding detection only. - const Ch* Peek4() const { - RAPIDJSON_ASSERT(sizeof(Ch) == 1); // Only usable for byte stream. - int i; - bool hasError = false; - for (i = 0; i < 4; ++i) { - typename StreamType::int_type c = stream_.get(); - if (c == StreamType::traits_type::eof()) { - hasError = true; - stream_.clear(); - break; - } - peekBuffer_[i] = static_cast(c); - } - for (--i; i >= 0; --i) - stream_.putback(peekBuffer_[i]); - return !hasError ? peekBuffer_ : 0; - } - -private: - BasicIStreamWrapper(const BasicIStreamWrapper&); - BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); - - StreamType& stream_; - size_t count_; //!< Number of characters read. Note: - mutable Ch peekBuffer_[4]; -}; - -typedef BasicIStreamWrapper IStreamWrapper; -typedef BasicIStreamWrapper WIStreamWrapper; - -#if defined(__clang__) || defined(_MSC_VER) -RAPIDJSON_DIAG_POP -#endif - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_ISTREAMWRAPPER_H_ diff --git a/contrib/rapidjson/include/rapidjson/memorybuffer.h b/contrib/rapidjson/include/rapidjson/memorybuffer.h index 39bee1dec..2484b2185 100644 --- a/contrib/rapidjson/include/rapidjson/memorybuffer.h +++ b/contrib/rapidjson/include/rapidjson/memorybuffer.h @@ -15,7 +15,7 @@ #ifndef RAPIDJSON_MEMORYBUFFER_H_ #define RAPIDJSON_MEMORYBUFFER_H_ -#include "stream.h" +#include "rapidjson.h" #include "internal/stack.h" RAPIDJSON_NAMESPACE_BEGIN diff --git a/contrib/rapidjson/include/rapidjson/memorystream.h b/contrib/rapidjson/include/rapidjson/memorystream.h index 1d71d8a4f..99feae5d7 100644 --- a/contrib/rapidjson/include/rapidjson/memorystream.h +++ b/contrib/rapidjson/include/rapidjson/memorystream.h @@ -15,13 +15,7 @@ #ifndef RAPIDJSON_MEMORYSTREAM_H_ #define RAPIDJSON_MEMORYSTREAM_H_ -#include "stream.h" - -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(unreachable-code) -RAPIDJSON_DIAG_OFF(missing-noreturn) -#endif +#include "rapidjson.h" RAPIDJSON_NAMESPACE_BEGIN @@ -42,8 +36,8 @@ struct MemoryStream { MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} - Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; } - Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; } + Ch Peek() const { return (src_ == end_) ? '\0' : *src_; } + Ch Take() { return (src_ == end_) ? '\0' : *src_++; } size_t Tell() const { return static_cast(src_ - begin_); } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } @@ -64,8 +58,4 @@ struct MemoryStream { RAPIDJSON_NAMESPACE_END -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - #endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/contrib/rapidjson/include/rapidjson/msinttypes/stdint.h b/contrib/rapidjson/include/rapidjson/msinttypes/stdint.h index 3d4477b9a..a26fff4bf 100644 --- a/contrib/rapidjson/include/rapidjson/msinttypes/stdint.h +++ b/contrib/rapidjson/include/rapidjson/msinttypes/stdint.h @@ -89,14 +89,14 @@ #include // For Visual Studio 6 in C++ mode and for many Visual Studio versions when -// compiling for ARM we have to wrap include with 'extern "C++" {}' -// or compiler would give many errors like this: +// compiling for ARM we should wrap include with 'extern "C++" {}' +// or compiler give many errors like this: // error C2733: second C linkage of overloaded function 'wmemchr' not allowed -#if defined(__cplusplus) && !defined(_M_ARM) +#ifdef __cplusplus extern "C" { #endif # include -#if defined(__cplusplus) && !defined(_M_ARM) +#ifdef __cplusplus } #endif diff --git a/contrib/rapidjson/include/rapidjson/ostreamwrapper.h b/contrib/rapidjson/include/rapidjson/ostreamwrapper.h deleted file mode 100644 index 6f4667c08..000000000 --- a/contrib/rapidjson/include/rapidjson/ostreamwrapper.h +++ /dev/null @@ -1,81 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_OSTREAMWRAPPER_H_ -#define RAPIDJSON_OSTREAMWRAPPER_H_ - -#include "stream.h" -#include - -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(padded) -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept. -/*! - The classes can be wrapped including but not limited to: - - - \c std::ostringstream - - \c std::stringstream - - \c std::wpstringstream - - \c std::wstringstream - - \c std::ifstream - - \c std::fstream - - \c std::wofstream - - \c std::wfstream - - \tparam StreamType Class derived from \c std::basic_ostream. -*/ - -template -class BasicOStreamWrapper { -public: - typedef typename StreamType::char_type Ch; - BasicOStreamWrapper(StreamType& stream) : stream_(stream) {} - - void Put(Ch c) { - stream_.put(c); - } - - void Flush() { - stream_.flush(); - } - - // Not implemented - char Peek() const { RAPIDJSON_ASSERT(false); return 0; } - char Take() { RAPIDJSON_ASSERT(false); return 0; } - size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } - char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } - -private: - BasicOStreamWrapper(const BasicOStreamWrapper&); - BasicOStreamWrapper& operator=(const BasicOStreamWrapper&); - - StreamType& stream_; -}; - -typedef BasicOStreamWrapper OStreamWrapper; -typedef BasicOStreamWrapper WOStreamWrapper; - -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_OSTREAMWRAPPER_H_ diff --git a/contrib/rapidjson/include/rapidjson/pointer.h b/contrib/rapidjson/include/rapidjson/pointer.h index 0f377efec..5d2aa8d63 100644 --- a/contrib/rapidjson/include/rapidjson/pointer.h +++ b/contrib/rapidjson/include/rapidjson/pointer.h @@ -18,16 +18,6 @@ #include "document.h" #include "internal/itoa.h" -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(switch-enum) -#endif - -#ifdef _MSC_VER -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated -#endif - RAPIDJSON_NAMESPACE_BEGIN static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token @@ -81,7 +71,7 @@ template class GenericPointer { public: typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value - typedef typename ValueType::Ch Ch; //!< Character type from Value + typedef typename EncodingType::Ch Ch; //!< Character type from Value //! A token is the basic units of internal representation. /*! @@ -106,7 +96,7 @@ public: //@{ //! Default constructor. - GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + GenericPointer() : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} //! Constructor that parses a string or URI fragment representation. /*! @@ -165,7 +155,7 @@ public: GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} //! Copy constructor. - GenericPointer(const GenericPointer& rhs, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + GenericPointer(const GenericPointer& rhs) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { *this = rhs; } @@ -240,7 +230,7 @@ public: template RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) Append(T* name, Allocator* allocator = 0) const { - return Append(name, internal::StrLen(name), allocator); + return Append(name, StrLen(name), allocator); } #if RAPIDJSON_HAS_STDSTRING @@ -263,18 +253,17 @@ public: */ GenericPointer Append(SizeType index, Allocator* allocator = 0) const { char buffer[21]; - char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer); - SizeType length = static_cast(end - buffer); + SizeType length = (sizeof(SizeType) == 4 ? internal::u32toa(index, buffer): internal::u64toa(index, buffer)) - buffer; buffer[length] = '\0'; if (sizeof(Ch) == 1) { - Token token = { reinterpret_cast(buffer), length, index }; + Token token = { (Ch*)buffer, length, index }; return Append(token, allocator); } else { Ch name[21]; for (size_t i = 0; i <= length; i++) - name[i] = static_cast(buffer[i]); + name[i] = buffer[i]; Token token = { name, length, index }; return Append(token, allocator); } @@ -282,7 +271,7 @@ public: //! Append a token by value, and return a new Pointer /*! - \param token token to be appended. + \param value Value (either Uint or String) to be appended. \param allocator Allocator for the newly return Pointer. \return A new Pointer with appended token. */ @@ -310,9 +299,6 @@ public: //@} - //! Get the allocator of this pointer. - Allocator& GetAllocator() { return *allocator_; } - //!@name Tokens //@{ @@ -404,7 +390,7 @@ public: bool exist = true; for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { if (v->IsArray() && t->name[0] == '-' && t->length == 1) { - v->PushBack(ValueType().Move(), allocator); + v->PushBack(Value().Move(), allocator); v = &((*v)[v->Size() - 1]); exist = false; } @@ -422,7 +408,7 @@ public: if (t->index >= v->Size()) { v->Reserve(t->index + 1, allocator); while (t->index >= v->Size()) - v->PushBack(ValueType().Move(), allocator); + v->PushBack(Value().Move(), allocator); exist = false; } v = &((*v)[t->index]); @@ -430,7 +416,7 @@ public: else { typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); if (m == v->MemberEnd()) { - v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); + v->AddMember(Value(t->name, t->length, allocator).Move(), Value().Move(), allocator); v = &(--v->MemberEnd())->value; // Assumes AddMember() appends at the end exist = false; } @@ -449,6 +435,7 @@ public: //! Creates a value in a document. /*! \param document A document to be resolved. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. \param alreadyExist If non-null, it stores whether the resolved value is already exist. \return The resolved newly created, or already exists value. */ @@ -465,18 +452,9 @@ public: //! Query a value in a subtree. /*! \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. - \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. \return Pointer to the value if it can be resolved. Otherwise null. - - \note - There are only 3 situations when a value cannot be resolved: - 1. A value in the path is not an array nor object. - 2. An object value does not contain the token. - 3. A token is out of range of an array value. - - Use unresolvedTokenIndex to retrieve the token index. */ - ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const { + ValueType* Get(ValueType& root) const { RAPIDJSON_ASSERT(IsValid()); ValueType* v = &root; for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { @@ -485,23 +463,18 @@ public: { typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); if (m == v->MemberEnd()) - break; + return 0; v = &m->value; } - continue; + break; case kArrayType: if (t->index == kPointerInvalidIndex || t->index >= v->Size()) - break; + return 0; v = &((*v)[t->index]); - continue; - default: break; + default: + return 0; } - - // Error: unresolved token - if (unresolvedTokenIndex) - *unresolvedTokenIndex = static_cast(t - tokens_); - return 0; } return v; } @@ -511,9 +484,7 @@ public: \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. \return Pointer to the value if it can be resolved. Otherwise null. */ - const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const { - return Get(const_cast(root), unresolvedTokenIndex); - } + const ValueType* Get(const ValueType& root) const { return Get(const_cast(root)); } //@} @@ -554,7 +525,7 @@ public: //! Query a value in a subtree with default primitive value. /*! - \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + \tparam T \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) @@ -584,7 +555,7 @@ public: //! Query a value in a document with default primitive value. /*! - \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + \tparam T \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) @@ -630,7 +601,7 @@ public: //! Set a primitive value in a subtree. /*! - \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + \tparam T \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) @@ -666,7 +637,7 @@ public: //! Set a primitive value in a document. /*! - \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + \tparam T \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) @@ -758,7 +729,7 @@ private: */ Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { if (!allocator_) // allocator is independently owned. - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) @@ -767,12 +738,8 @@ private: tokenCount_ = rhs.tokenCount_ + extraToken; tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch))); nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); - if (rhs.tokenCount_ > 0) { - std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); - } - if (nameBufferSize > 0) { - std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); - } + std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); + std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); // Adjust pointers to name buffer std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_; @@ -792,13 +759,11 @@ private: } //! Parse a JSON String or its URI fragment representation into tokens. -#ifndef __clang__ // -Wdocumentation /*! \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated. \param length Length of the source string. \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped. */ -#endif void Parse(const Ch* source, size_t length) { RAPIDJSON_ASSERT(source != NULL); RAPIDJSON_ASSERT(nameBuffer_ == 0); @@ -806,7 +771,7 @@ private: // Create own allocator if user did not supply. if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); // Count number of '/' as tokenCount tokenCount_ = 0; @@ -892,7 +857,7 @@ private: *name++ = c; } - token->length = static_cast(name - token->name); + token->length = name - token->name; if (token->length == 0) isNumber = false; *name++ = '\0'; // Null terminator @@ -979,8 +944,6 @@ private: */ class PercentDecodeStream { public: - typedef typename ValueType::Ch Ch; - //! Constructor /*! \param source Start of the stream @@ -996,11 +959,11 @@ private: src_++; Ch c = 0; for (int j = 0; j < 2; j++) { - c = static_cast(c << 4); + c <<= 4; Ch h = *src_; - if (h >= '0' && h <= '9') c = static_cast(c + h - '0'); - else if (h >= 'A' && h <= 'F') c = static_cast(c + h - 'A' + 10); - else if (h >= 'a' && h <= 'f') c = static_cast(c + h - 'a' + 10); + if (h >= '0' && h <= '9') c += h - '0'; + else if (h >= 'A' && h <= 'F') c += h - 'A' + 10; + else if (h >= 'a' && h <= 'f') c += h - 'a' + 10; else { valid_ = false; return 0; @@ -1010,7 +973,7 @@ private: return c; } - size_t Tell() const { return static_cast(src_ - head_); } + size_t Tell() const { return src_ - head_; } bool IsValid() const { return valid_; } private: @@ -1029,8 +992,8 @@ private: unsigned char u = static_cast(c); static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; os_.Put('%'); - os_.Put(static_cast(hexDigits[u >> 4])); - os_.Put(static_cast(hexDigits[u & 15])); + os_.Put(hexDigits[u >> 4]); + os_.Put(hexDigits[u & 15]); } private: OutputStream& os_; @@ -1078,23 +1041,23 @@ typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, c ////////////////////////////////////////////////////////////////////////////// template -typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { - return pointer.Get(root, unresolvedTokenIndex); +typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer) { + return pointer.Get(root); } template -const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { - return pointer.Get(root, unresolvedTokenIndex); +const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer) { + return pointer.Get(root); } template -typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) { - return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N]) { + return GenericPointer(source, N - 1).Get(root); } template -const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) { - return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Get(root); } ////////////////////////////////////////////////////////////////////////////// @@ -1347,12 +1310,4 @@ bool EraseValueByPointer(T& root, const CharType(&source)[N]) { RAPIDJSON_NAMESPACE_END -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - -#ifdef _MSC_VER -RAPIDJSON_DIAG_POP -#endif - #endif // RAPIDJSON_POINTER_H_ diff --git a/contrib/rapidjson/include/rapidjson/prettywriter.h b/contrib/rapidjson/include/rapidjson/prettywriter.h index 98dfb3060..416dd492e 100644 --- a/contrib/rapidjson/include/rapidjson/prettywriter.h +++ b/contrib/rapidjson/include/rapidjson/prettywriter.h @@ -22,21 +22,8 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif -#if defined(__clang__) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(c++98-compat) -#endif - RAPIDJSON_NAMESPACE_BEGIN -//! Combination of PrettyWriter format flags. -/*! \see PrettyWriter::SetFormatOptions - */ -enum PrettyFormatOptions { - kFormatDefault = 0, //!< Default pretty formatting. - kFormatSingleLineArray = 1 //!< Format arrays on a single line. -}; - //! Writer with indentation and spacing. /*! \tparam OutputStream Type of ouptut os. @@ -44,10 +31,10 @@ enum PrettyFormatOptions { \tparam TargetEncoding Encoding of output stream. \tparam StackAllocator Type of allocator for allocating memory of stack. */ -template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> -class PrettyWriter : public Writer { +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator> +class PrettyWriter : public Writer { public: - typedef Writer Base; + typedef Writer Base; typedef typename Base::Ch Ch; //! Constructor @@ -55,17 +42,8 @@ public: \param allocator User supplied allocator. If it is null, it will create a private one. \param levelDepth Initial capacity of stack. */ - explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : - Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} - - - explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : - Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - PrettyWriter(PrettyWriter&& rhs) : - Base(std::forward(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {} -#endif + PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} //! Set custom indentation. /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). @@ -79,14 +57,6 @@ public: return *this; } - //! Set pretty writer formatting options. - /*! \param options Formatting options. - */ - PrettyWriter& SetFormatOptions(PrettyFormatOptions options) { - formatOptions_ = options; - return *this; - } - /*! @name Implementation of Handler \see Handler */ @@ -100,15 +70,7 @@ public: bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } - bool RawNumber(const Ch* str, SizeType length, bool copy = false) { - RAPIDJSON_ASSERT(str != 0); - (void)copy; - PrettyPrefix(kNumberType); - return Base::WriteString(str, length); - } - bool String(const Ch* str, SizeType length, bool copy = false) { - RAPIDJSON_ASSERT(str != 0); (void)copy; PrettyPrefix(kStringType); return Base::WriteString(str, length); @@ -127,19 +89,11 @@ public: } bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } - -#if RAPIDJSON_HAS_STDSTRING - bool Key(const std::basic_string& str) { - return Key(str.data(), SizeType(str.size())); - } -#endif bool EndObject(SizeType memberCount = 0) { (void)memberCount; - RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object - RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); // currently inside an Array, not Object - RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value - + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; if (!empty) { @@ -150,7 +104,7 @@ public: (void)ret; RAPIDJSON_ASSERT(ret == true); if (Base::level_stack_.Empty()) // end of json text - Base::Flush(); + Base::os_->Flush(); return true; } @@ -166,7 +120,7 @@ public: RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; - if (!empty && !(formatOptions_ & kFormatSingleLineArray)) { + if (!empty) { Base::os_->Put('\n'); WriteIndent(); } @@ -174,7 +128,7 @@ public: (void)ret; RAPIDJSON_ASSERT(ret == true); if (Base::level_stack_.Empty()) // end of json text - Base::Flush(); + Base::os_->Flush(); return true; } @@ -188,22 +142,6 @@ public: bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } //@} - - //! Write a raw JSON value. - /*! - For user to write a stringified JSON as a value. - - \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. - \param length Length of the json. - \param type Type of the root of json. - \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. - */ - bool RawValue(const Ch* json, size_t length, Type type) { - RAPIDJSON_ASSERT(json != 0); - PrettyPrefix(type); - return Base::WriteRawValue(json, length); - } - protected: void PrettyPrefix(Type type) { (void)type; @@ -213,14 +151,11 @@ protected: if (level->inArray) { if (level->valueCount > 0) { Base::os_->Put(','); // add comma if it is not the first element in array - if (formatOptions_ & kFormatSingleLineArray) - Base::os_->Put(' '); - } - - if (!(formatOptions_ & kFormatSingleLineArray)) { Base::os_->Put('\n'); - WriteIndent(); } + else + Base::os_->Put('\n'); + WriteIndent(); } else { // in object if (level->valueCount > 0) { @@ -251,12 +186,11 @@ protected: void WriteIndent() { size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; - PutN(*Base::os_, static_cast(indentChar_), count); + PutN(*Base::os_, indentChar_, count); } Ch indentChar_; unsigned indentCharCount_; - PrettyFormatOptions formatOptions_; private: // Prohibit copy constructor & assignment operator. @@ -266,10 +200,6 @@ private: RAPIDJSON_NAMESPACE_END -#if defined(__clang__) -RAPIDJSON_DIAG_POP -#endif - #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif diff --git a/contrib/rapidjson/include/rapidjson/rapidjson.h b/contrib/rapidjson/include/rapidjson/rapidjson.h index 5716fdc06..f22130d3c 100644 --- a/contrib/rapidjson/include/rapidjson/rapidjson.h +++ b/contrib/rapidjson/include/rapidjson/rapidjson.h @@ -49,11 +49,6 @@ // token stringification #define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) #define RAPIDJSON_DO_STRINGIFY(x) #x - -// token concatenation -#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) -#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) -#define RAPIDJSON_DO_JOIN2(X, Y) X##Y //!@endcond /*! \def RAPIDJSON_MAJOR_VERSION @@ -73,8 +68,8 @@ \brief Version of RapidJSON in ".." string format. */ #define RAPIDJSON_MAJOR_VERSION 1 -#define RAPIDJSON_MINOR_VERSION 1 -#define RAPIDJSON_PATCH_VERSION 0 +#define RAPIDJSON_MINOR_VERSION 0 +#define RAPIDJSON_PATCH_VERSION 2 #define RAPIDJSON_VERSION_STRING \ RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION) @@ -124,31 +119,6 @@ #define RAPIDJSON_NAMESPACE_END } #endif -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_HAS_STDSTRING - -#ifndef RAPIDJSON_HAS_STDSTRING -#ifdef RAPIDJSON_DOXYGEN_RUNNING -#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation -#else -#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default -#endif -/*! \def RAPIDJSON_HAS_STDSTRING - \ingroup RAPIDJSON_CONFIG - \brief Enable RapidJSON support for \c std::string - - By defining this preprocessor symbol to \c 1, several convenience functions for using - \ref rapidjson::GenericValue with \c std::string are enabled, especially - for construction and comparison. - - \hideinitializer -*/ -#endif // !defined(RAPIDJSON_HAS_STDSTRING) - -#if RAPIDJSON_HAS_STDSTRING -#include -#endif // RAPIDJSON_HAS_STDSTRING - /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_NO_INT64DEFINE @@ -164,7 +134,7 @@ */ #ifndef RAPIDJSON_NO_INT64DEFINE //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013 +#ifdef _MSC_VER #include "msinttypes/stdint.h" #include "msinttypes/inttypes.h" #else @@ -183,9 +153,9 @@ #ifndef RAPIDJSON_FORCEINLINE //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#if defined(_MSC_VER) && defined(NDEBUG) +#if defined(_MSC_VER) && !defined(NDEBUG) #define RAPIDJSON_FORCEINLINE __forceinline -#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG) +#elif defined(__GNUC__) && __GNUC__ >= 4 && !defined(NDEBUG) #define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) #else #define RAPIDJSON_FORCEINLINE @@ -241,8 +211,6 @@ # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN -# elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64)) -# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN # elif defined(RAPIDJSON_DOXYGEN_RUNNING) # define RAPIDJSON_ENDIAN # else @@ -255,7 +223,7 @@ //! Whether using 64-bit architecture #ifndef RAPIDJSON_64BIT -#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__) +#if defined(__LP64__) || defined(_WIN64) #define RAPIDJSON_64BIT 1 #else #define RAPIDJSON_64BIT 0 @@ -270,14 +238,13 @@ \param x pointer to align Some machines require strict data alignment. Currently the default uses 4 bytes - alignment on 32-bit platforms and 8 bytes alignment for 64-bit platforms. - User can customize by defining the RAPIDJSON_ALIGN function macro. + alignment. User can customize by defining the RAPIDJSON_ALIGN function macro., */ #ifndef RAPIDJSON_ALIGN #if RAPIDJSON_64BIT == 1 -#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) +#define RAPIDJSON_ALIGN(x) ((x + 7u) & ~7u) #else -#define RAPIDJSON_ALIGN(x) (((x) + 3u) & ~3u) +#define RAPIDJSON_ALIGN(x) ((x + 3u) & ~3u) #endif #endif @@ -295,47 +262,17 @@ #endif /////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_48BITPOINTER_OPTIMIZATION - -//! Use only lower 48-bit address for some pointers. -/*! - \ingroup RAPIDJSON_CONFIG - - This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address. - The higher 16-bit can be used for storing other data. - \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture. -*/ -#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION -#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) -#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1 -#else -#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 -#endif -#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION - -#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1 -#if RAPIDJSON_64BIT != 1 -#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1 -#endif -#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast((reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast(reinterpret_cast(x)))) -#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast(reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF)))) -#else -#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x)) -#define RAPIDJSON_GETPOINTER(type, p) (p) -#endif - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD /*! \def RAPIDJSON_SIMD \ingroup RAPIDJSON_CONFIG - \brief Enable SSE2/SSE4.2/Neon optimization. + \brief Enable SSE2/SSE4.2 optimization. RapidJSON supports optimized implementations for some parsing operations - based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel - or ARM compatible processors. + based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible + processors. - To enable these optimizations, three different symbols can be defined; + To enable these optimizations, two different symbols can be defined; \code // Enable SSE2 optimization. #define RAPIDJSON_SSE2 @@ -344,17 +281,13 @@ #define RAPIDJSON_SSE42 \endcode - // Enable ARM Neon optimization. - #define RAPIDJSON_NEON - \endcode - - \c RAPIDJSON_SSE42 takes precedence over SSE2, if both are defined. + \c RAPIDJSON_SSE42 takes precedence, if both are defined. If any of these symbols is defined, RapidJSON defines the macro \c RAPIDJSON_SIMD to indicate the availability of the optimized code. */ #if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ - || defined(RAPIDJSON_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING) + || defined(RAPIDJSON_DOXYGEN_RUNNING) #define RAPIDJSON_SIMD #endif @@ -414,33 +347,25 @@ RAPIDJSON_NAMESPACE_END /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_STATIC_ASSERT -// Prefer C++11 static_assert, if available +// Adopt from boost #ifndef RAPIDJSON_STATIC_ASSERT -#if __cplusplus >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 ) -#define RAPIDJSON_STATIC_ASSERT(x) \ - static_assert(x, RAPIDJSON_STRINGIFY(x)) -#endif // C++11 -#endif // RAPIDJSON_STATIC_ASSERT - -// Adopt C++03 implementation from boost -#ifndef RAPIDJSON_STATIC_ASSERT -#ifndef __clang__ //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#endif RAPIDJSON_NAMESPACE_BEGIN template struct STATIC_ASSERTION_FAILURE; template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; -template struct StaticAssertTest {}; +template struct StaticAssertTest {}; RAPIDJSON_NAMESPACE_END +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y + #if defined(__GNUC__) #define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) #else #define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE #endif -#ifndef __clang__ //!@endcond -#endif /*! \def RAPIDJSON_STATIC_ASSERT \brief (Internal) macro to check for conditions at compile-time @@ -451,35 +376,6 @@ RAPIDJSON_NAMESPACE_END typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE -#endif // RAPIDJSON_STATIC_ASSERT - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY - -//! Compiler branching hint for expression with high probability to be true. -/*! - \ingroup RAPIDJSON_CONFIG - \param x Boolean expression likely to be true. -*/ -#ifndef RAPIDJSON_LIKELY -#if defined(__GNUC__) || defined(__clang__) -#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1) -#else -#define RAPIDJSON_LIKELY(x) (x) -#endif -#endif - -//! Compiler branching hint for expression with low probability to be true. -/*! - \ingroup RAPIDJSON_CONFIG - \param x Boolean expression unlikely to be true. -*/ -#ifndef RAPIDJSON_UNLIKELY -#if defined(__GNUC__) || defined(__clang__) -#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0) -#else -#define RAPIDJSON_UNLIKELY(x) (x) -#endif #endif /////////////////////////////////////////////////////////////////////////////// @@ -542,12 +438,8 @@ RAPIDJSON_NAMESPACE_END #ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS #if defined(__clang__) -#if __has_feature(cxx_rvalue_references) && \ +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS __has_feature(cxx_rvalue_references) && \ (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) -#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 -#else -#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 -#endif #elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ (defined(_MSC_VER) && _MSC_VER >= 1600) @@ -578,17 +470,6 @@ RAPIDJSON_NAMESPACE_END #define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 #endif -#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR -#if defined(__clang__) -#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) -#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ - (defined(_MSC_VER) && _MSC_VER >= 1700) -#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 -#else -#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 -#endif -#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR - //!@endcond /////////////////////////////////////////////////////////////////////////////// @@ -596,7 +477,7 @@ RAPIDJSON_NAMESPACE_END #ifndef RAPIDJSON_NEW ///! customization point for global \c new -#define RAPIDJSON_NEW(TypeName) new TypeName +#define RAPIDJSON_NEW(x) new x #endif #ifndef RAPIDJSON_DELETE ///! customization point for global \c delete @@ -604,7 +485,10 @@ RAPIDJSON_NAMESPACE_END #endif /////////////////////////////////////////////////////////////////////////////// -// Type +// Allocators and Encodings + +#include "allocators.h" +#include "encodings.h" /*! \namespace rapidjson \brief main RapidJSON namespace @@ -612,6 +496,148 @@ RAPIDJSON_NAMESPACE_END */ RAPIDJSON_NAMESPACE_BEGIN +/////////////////////////////////////////////////////////////////////////////// +// Stream + +/*! \class rapidjson::Stream + \brief Concept for reading and writing characters. + + For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). + + For write-only stream, only need to implement Put() and Flush(). + +\code +concept Stream { + typename Ch; //!< Character type of the stream. + + //! Read the current character from stream without moving the read cursor. + Ch Peek() const; + + //! Read the current character from stream and moving the read cursor to next character. + Ch Take(); + + //! Get the current read cursor. + //! \return Number of characters read from start. + size_t Tell(); + + //! Begin writing operation at the current read pointer. + //! \return The begin writer pointer. + Ch* PutBegin(); + + //! Write a character. + void Put(Ch c); + + //! Flush the buffer. + void Flush(); + + //! End the writing operation. + //! \param begin The begin write pointer returned by PutBegin(). + //! \return Number of characters written. + size_t PutEnd(Ch* begin); +} +\endcode +*/ + +//! Provides additional information for stream. +/*! + By using traits pattern, this type provides a default configuration for stream. + For custom stream, this type can be specialized for other configuration. + See TEST(Reader, CustomStringStream) in readertest.cpp for example. +*/ +template +struct StreamTraits { + //! Whether to make local copy of stream for optimization during parsing. + /*! + By default, for safety, streams do not use local copy optimization. + Stream that can be copied fast should specialize this, like StreamTraits. + */ + enum { copyOptimization = 0 }; +}; + +//! Put N copies of a character to a stream. +template +inline void PutN(Stream& stream, Ch c, size_t n) { + for (size_t i = 0; i < n; i++) + stream.Put(c); +} + +/////////////////////////////////////////////////////////////////////////////// +// StringStream + +//! Read-only string stream. +/*! \note implements Stream concept +*/ +template +struct GenericStringStream { + typedef typename Encoding::Ch Ch; + + GenericStringStream(const Ch *src) : src_(src), head_(src) {} + + Ch Peek() const { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() const { return static_cast(src_ - head_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! String stream with UTF8 encoding. +typedef GenericStringStream > StringStream; + +/////////////////////////////////////////////////////////////////////////////// +// InsituStringStream + +//! A read-write string stream. +/*! This string stream is particularly designed for in-situ parsing. + \note implements Stream concept +*/ +template +struct GenericInsituStringStream { + typedef typename Encoding::Ch Ch; + + GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} + + // Read + Ch Peek() { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() { return static_cast(src_ - head_); } + + // Write + void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } + + Ch* PutBegin() { return dst_ = src_; } + size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } + void Flush() {} + + Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } + void Pop(size_t count) { dst_ -= count; } + + Ch* src_; + Ch* dst_; + Ch* head_; +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! Insitu string stream with UTF8 encoding. +typedef GenericInsituStringStream > InsituStringStream; + +/////////////////////////////////////////////////////////////////////////////// +// Type + //! Type of JSON value enum Type { kNullType = 0, //!< null diff --git a/contrib/rapidjson/include/rapidjson/reader.h b/contrib/rapidjson/include/rapidjson/reader.h index 120c31115..c5ecf4be5 100644 --- a/contrib/rapidjson/include/rapidjson/reader.h +++ b/contrib/rapidjson/include/rapidjson/reader.h @@ -1,5 +1,5 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// +// // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except @@ -7,9 +7,9 @@ // // http://opensource.org/licenses/MIT // -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_READER_H_ @@ -17,13 +17,11 @@ /*! \file reader.h */ -#include "allocators.h" -#include "stream.h" -#include "encodedstream.h" +#include "rapidjson.h" +#include "encodings.h" #include "internal/meta.h" #include "internal/stack.h" #include "internal/strtod.h" -#include #if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) #include @@ -33,8 +31,6 @@ #include #elif defined(RAPIDJSON_SSE2) #include -#elif defined(RAPIDJSON_NEON) -#include #endif #ifdef _MSC_VER @@ -43,13 +39,6 @@ RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant RAPIDJSON_DIAG_OFF(4702) // unreachable code #endif -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(old-style-cast) -RAPIDJSON_DIAG_OFF(padded) -RAPIDJSON_DIAG_OFF(switch-enum) -#endif - #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) @@ -60,7 +49,7 @@ RAPIDJSON_DIAG_OFF(effc++) #ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN #define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ RAPIDJSON_MULTILINEMACRO_BEGIN \ - if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \ + if (HasParseError()) { return value; } \ RAPIDJSON_MULTILINEMACRO_END #endif #define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ @@ -131,7 +120,7 @@ RAPIDJSON_NAMESPACE_BEGIN /////////////////////////////////////////////////////////////////////////////// // ParseFlag -/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS +/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS \ingroup RAPIDJSON_CONFIG \brief User-defined kParseDefaultFlags definition. @@ -151,10 +140,6 @@ enum ParseFlag { kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). - kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments. - kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings. - kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays. - kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles. kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS }; @@ -163,7 +148,7 @@ enum ParseFlag { /*! \class rapidjson::Handler \brief Concept for receiving events from GenericReader upon parsing. - The functions return true if no error occurs. If they return false, + The functions return true if no error occurs. If they return false, the event publisher should terminate the process. \code concept Handler { @@ -176,8 +161,6 @@ concept Handler { bool Int64(int64_t i); bool Uint64(uint64_t i); bool Double(double d); - /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) - bool RawNumber(const Ch* str, SizeType length, bool copy); bool String(const Ch* str, SizeType length, bool copy); bool StartObject(); bool Key(const Ch* str, SizeType length, bool copy); @@ -208,8 +191,6 @@ struct BaseReaderHandler { bool Int64(int64_t) { return static_cast(*this).Default(); } bool Uint64(uint64_t) { return static_cast(*this).Default(); } bool Double(double) { return static_cast(*this).Default(); } - /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) - bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } bool StartObject() { return static_cast(*this).Default(); } bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } @@ -267,17 +248,10 @@ void SkipWhitespace(InputStream& is) { internal::StreamLocalCopy copy(is); InputStream& s(copy.s); - typename InputStream::Ch c; - while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t') + while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t') s.Take(); } -inline const char* SkipWhitespace(const char* p, const char* end) { - while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) - ++p; - return p; -} - #ifdef RAPIDJSON_SSE42 //! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. inline const char *SkipWhitespace_SIMD(const char* p) { @@ -288,7 +262,7 @@ inline const char *SkipWhitespace_SIMD(const char* p) { return p; // 16-byte align to the next boundary - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & ~15); while (p != nextAligned) if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') ++p; @@ -297,37 +271,23 @@ inline const char *SkipWhitespace_SIMD(const char* p) { // The rest of string using SIMD static const char whitespace[16] = " \n\r\t"; - const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + const __m128i w = _mm_load_si128((const __m128i *)&whitespace[0]); for (;; p += 16) { - const __m128i s = _mm_load_si128(reinterpret_cast(p)); - const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); - if (r != 16) // some of characters is non-whitespace - return p + r; + const __m128i s = _mm_load_si128((const __m128i *)p); + const unsigned r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); + if (r != 0) { // some of characters is non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } } } -inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { - // Fast return for single non-whitespace - if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) - ++p; - else - return p; - - // The middle of string using SIMD - static const char whitespace[16] = " \n\r\t"; - const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); - - for (; p <= end - 16; p += 16) { - const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); - const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); - if (r != 16) // some of characters is non-whitespace - return p + r; - } - - return SkipWhitespace(p, end); -} - #elif defined(RAPIDJSON_SSE2) //! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. @@ -339,7 +299,7 @@ inline const char *SkipWhitespace_SIMD(const char* p) { return p; // 16-byte align to the next boundary - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & ~15); while (p != nextAligned) if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') ++p; @@ -347,22 +307,24 @@ inline const char *SkipWhitespace_SIMD(const char* p) { return p; // The rest of string - #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } - static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; - #undef C16 + static const char whitespaces[4][17] = { + " ", + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"}; - const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); - const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); - const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); - const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + const __m128i w0 = _mm_loadu_si128((const __m128i *)&whitespaces[0][0]); + const __m128i w1 = _mm_loadu_si128((const __m128i *)&whitespaces[1][0]); + const __m128i w2 = _mm_loadu_si128((const __m128i *)&whitespaces[2][0]); + const __m128i w3 = _mm_loadu_si128((const __m128i *)&whitespaces[3][0]); for (;; p += 16) { - const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i s = _mm_load_si128((const __m128i *)p); __m128i x = _mm_cmpeq_epi8(s, w0); x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); - unsigned short r = static_cast(~_mm_movemask_epi8(x)); + unsigned short r = (unsigned short)~_mm_movemask_epi8(x); if (r != 0) { // some of characters may be non-whitespace #ifdef _MSC_VER // Find the index of first non-whitespace unsigned long offset; @@ -375,134 +337,11 @@ inline const char *SkipWhitespace_SIMD(const char* p) { } } -inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { - // Fast return for single non-whitespace - if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) - ++p; - else - return p; - - // The rest of string - #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } - static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; - #undef C16 - - const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); - const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); - const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); - const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); - - for (; p <= end - 16; p += 16) { - const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); - __m128i x = _mm_cmpeq_epi8(s, w0); - x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); - x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); - x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); - unsigned short r = static_cast(~_mm_movemask_epi8(x)); - if (r != 0) { // some of characters may be non-whitespace -#ifdef _MSC_VER // Find the index of first non-whitespace - unsigned long offset; - _BitScanForward(&offset, r); - return p + offset; -#else - return p + __builtin_ffs(r) - 1; -#endif - } - } - - return SkipWhitespace(p, end); -} - -#elif defined(RAPIDJSON_NEON) - -//! Skip whitespace with ARM Neon instructions, testing 16 8-byte characters at once. -inline const char *SkipWhitespace_SIMD(const char* p) { - // Fast return for single non-whitespace - if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') - ++p; - else - return p; - - // 16-byte align to the next boundary - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - while (p != nextAligned) - if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') - ++p; - else - return p; - - const uint8x16_t w0 = vmovq_n_u8(' '); - const uint8x16_t w1 = vmovq_n_u8('\n'); - const uint8x16_t w2 = vmovq_n_u8('\r'); - const uint8x16_t w3 = vmovq_n_u8('\t'); - - for (;; p += 16) { - const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); - uint8x16_t x = vceqq_u8(s, w0); - x = vorrq_u8(x, vceqq_u8(s, w1)); - x = vorrq_u8(x, vceqq_u8(s, w2)); - x = vorrq_u8(x, vceqq_u8(s, w3)); - - x = vmvnq_u8(x); // Negate - x = vrev64q_u8(x); // Rev in 64 - uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract - uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract - - if (low == 0) { - if (high != 0) { - int lz =__builtin_clzll(high);; - return p + 8 + (lz >> 3); - } - } else { - int lz = __builtin_clzll(low);; - return p + (lz >> 3); - } - } -} - -inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { - // Fast return for single non-whitespace - if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) - ++p; - else - return p; - - const uint8x16_t w0 = vmovq_n_u8(' '); - const uint8x16_t w1 = vmovq_n_u8('\n'); - const uint8x16_t w2 = vmovq_n_u8('\r'); - const uint8x16_t w3 = vmovq_n_u8('\t'); - - for (; p <= end - 16; p += 16) { - const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); - uint8x16_t x = vceqq_u8(s, w0); - x = vorrq_u8(x, vceqq_u8(s, w1)); - x = vorrq_u8(x, vceqq_u8(s, w2)); - x = vorrq_u8(x, vceqq_u8(s, w3)); - - x = vmvnq_u8(x); // Negate - x = vrev64q_u8(x); // Rev in 64 - uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract - uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract - - if (low == 0) { - if (high != 0) { - int lz = __builtin_clzll(high); - return p + 8 + (lz >> 3); - } - } else { - int lz = __builtin_clzll(low); - return p + (lz >> 3); - } - } - - return SkipWhitespace(p, end); -} - -#endif // RAPIDJSON_NEON +#endif // RAPIDJSON_SSE2 #ifdef RAPIDJSON_SIMD //! Template function specialization for InsituStringStream -template<> inline void SkipWhitespace(InsituStringStream& is) { +template<> inline void SkipWhitespace(InsituStringStream& is) { is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); } @@ -510,27 +349,23 @@ template<> inline void SkipWhitespace(InsituStringStream& is) { template<> inline void SkipWhitespace(StringStream& is) { is.src_ = SkipWhitespace_SIMD(is.src_); } - -template<> inline void SkipWhitespace(EncodedInputStream, MemoryStream>& is) { - is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_); -} #endif // RAPIDJSON_SIMD /////////////////////////////////////////////////////////////////////////////// // GenericReader //! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. -/*! GenericReader parses JSON text from a stream, and send events synchronously to an +/*! GenericReader parses JSON text from a stream, and send events synchronously to an object implementing Handler concept. - It needs to allocate a stack for storing a single decoded string during + It needs to allocate a stack for storing a single decoded string during non-destructive parsing. - For in-situ parsing, the decoded string is directly written to the source + For in-situ parsing, the decoded string is directly written to the source text string, no temporary buffer is required. A GenericReader object can be reused for parsing multiple JSON text. - + \tparam SourceEncoding Encoding of the input stream. \tparam TargetEncoding Encoding of the parse output. \tparam StackAllocator Allocator type for stack. @@ -563,10 +398,9 @@ public: ClearStackOnExit scope(*this); - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + SkipWhitespace(is); - if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) { + if (is.Peek() == '\0') { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); } @@ -575,10 +409,9 @@ public: RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); if (!(parseFlags & kParseStopWhenDoneFlag)) { - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + SkipWhitespace(is); - if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) { + if (is.Peek() != '\0') { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); } @@ -600,86 +433,9 @@ public: return Parse(is, handler); } - //! Initialize JSON text token-by-token parsing - /*! - */ - void IterativeParseInit() { - parseResult_.Clear(); - state_ = IterativeParsingStartState; - } - - //! Parse one token from JSON text - /*! \tparam InputStream Type of input stream, implementing Stream concept - \tparam Handler Type of handler, implementing Handler concept. - \param is Input stream to be parsed. - \param handler The handler to receive events. - \return Whether the parsing is successful. - */ - template - bool IterativeParseNext(InputStream& is, Handler& handler) { - while (RAPIDJSON_LIKELY(is.Peek() != '\0')) { - SkipWhitespaceAndComments(is); - - Token t = Tokenize(is.Peek()); - IterativeParsingState n = Predict(state_, t); - IterativeParsingState d = Transit(state_, t, n, is, handler); - - // If we've finished or hit an error... - if (RAPIDJSON_UNLIKELY(IsIterativeParsingCompleteState(d))) { - // Report errors. - if (d == IterativeParsingErrorState) { - HandleError(state_, is); - return false; - } - - // Transition to the finish state. - RAPIDJSON_ASSERT(d == IterativeParsingFinishState); - state_ = d; - - // If StopWhenDone is not set... - if (!(parseFlags & kParseStopWhenDoneFlag)) { - // ... and extra non-whitespace data is found... - SkipWhitespaceAndComments(is); - if (is.Peek() != '\0') { - // ... this is considered an error. - HandleError(state_, is); - return false; - } - } - - // Success! We are done! - return true; - } - - // Transition to the new state. - state_ = d; - - // If we parsed anything other than a delimiter, we invoked the handler, so we can return true now. - if (!IsIterativeParsingDelimiterState(n)) - return true; - } - - // We reached the end of file. - stack_.Clear(); - - if (state_ != IterativeParsingFinishState) { - HandleError(state_, is); - return false; - } - - return true; - } - - //! Check if token-by-token parsing JSON text is complete - /*! \return Whether the JSON has been fully decoded. - */ - RAPIDJSON_FORCEINLINE bool IterativeParseComplete() { - return IsIterativeParsingCompleteState(state_); - } - //! Whether a parse error has occured in the last parsing. bool HasParseError() const { return parseResult_.IsError(); } - + //! Get the \ref ParseErrorCode of last parsing. ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } @@ -706,98 +462,52 @@ private: ClearStackOnExit& operator=(const ClearStackOnExit&); }; - template - void SkipWhitespaceAndComments(InputStream& is) { - SkipWhitespace(is); - - if (parseFlags & kParseCommentsFlag) { - while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) { - if (Consume(is, '*')) { - while (true) { - if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) - RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); - else if (Consume(is, '*')) { - if (Consume(is, '/')) - break; - } - else - is.Take(); - } - } - else if (RAPIDJSON_LIKELY(Consume(is, '/'))) - while (is.Peek() != '\0' && is.Take() != '\n') {} - else - RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); - - SkipWhitespace(is); - } - } - } - // Parse object: { string : value, ... } template void ParseObject(InputStream& is, Handler& handler) { RAPIDJSON_ASSERT(is.Peek() == '{'); is.Take(); // Skip '{' - - if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + + if (!handler.StartObject()) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + SkipWhitespace(is); - if (Consume(is, '}')) { - if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object + if (is.Peek() == '}') { + is.Take(); + if (!handler.EndObject(0)) // empty object RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); return; } for (SizeType memberCount = 0;;) { - if (RAPIDJSON_UNLIKELY(is.Peek() != '"')) + if (is.Peek() != '"') RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); ParseString(is, handler, true); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + SkipWhitespace(is); - if (RAPIDJSON_UNLIKELY(!Consume(is, ':'))) + if (is.Take() != ':') RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + SkipWhitespace(is); ParseValue(is, handler); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + SkipWhitespace(is); ++memberCount; - switch (is.Peek()) { - case ',': - is.Take(); - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - break; - case '}': - is.Take(); - if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + switch (is.Take()) { + case ',': SkipWhitespace(is); break; + case '}': + if (!handler.EndObject(memberCount)) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); return; - default: - RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy - } - - if (parseFlags & kParseTrailingCommasFlag) { - if (is.Peek() == '}') { - if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - is.Take(); - return; - } + default: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); } } } @@ -807,15 +517,15 @@ private: void ParseArray(InputStream& is, Handler& handler) { RAPIDJSON_ASSERT(is.Peek() == '['); is.Take(); // Skip '[' - - if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + + if (!handler.StartArray()) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespace(is); - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - - if (Consume(is, ']')) { - if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array + if (is.Peek() == ']') { + is.Take(); + if (!handler.EndArray(0)) // empty array RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); return; } @@ -825,28 +535,15 @@ private: RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; ++elementCount; - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + SkipWhitespace(is); - if (Consume(is, ',')) { - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - } - else if (Consume(is, ']')) { - if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - return; - } - else - RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); - - if (parseFlags & kParseTrailingCommasFlag) { - if (is.Peek() == ']') { - if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + switch (is.Take()) { + case ',': SkipWhitespace(is); break; + case ']': + if (!handler.EndArray(elementCount)) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - is.Take(); return; - } + default: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); } } } @@ -856,12 +553,12 @@ private: RAPIDJSON_ASSERT(is.Peek() == 'n'); is.Take(); - if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) { - if (RAPIDJSON_UNLIKELY(!handler.Null())) + if (is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l') { + if (!handler.Null()) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); } else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); } template @@ -869,12 +566,12 @@ private: RAPIDJSON_ASSERT(is.Peek() == 't'); is.Take(); - if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) { - if (RAPIDJSON_UNLIKELY(!handler.Bool(true))) + if (is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e') { + if (!handler.Bool(true)) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); } else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); } template @@ -882,30 +579,20 @@ private: RAPIDJSON_ASSERT(is.Peek() == 'f'); is.Take(); - if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) { - if (RAPIDJSON_UNLIKELY(!handler.Bool(false))) + if (is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e') { + if (!handler.Bool(false)) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); } else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); - } - - template - RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) { - if (RAPIDJSON_LIKELY(is.Peek() == expect)) { - is.Take(); - return true; - } - else - return false; + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); } // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). template - unsigned ParseHex4(InputStream& is, size_t escapeOffset) { + unsigned ParseHex4(InputStream& is) { unsigned codepoint = 0; for (int i = 0; i < 4; i++) { - Ch c = is.Peek(); + Ch c = is.Take(); codepoint <<= 4; codepoint += static_cast(c); if (c >= '0' && c <= '9') @@ -915,10 +602,9 @@ private: else if (c >= 'a' && c <= 'f') codepoint -= 'a' - 10; else { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset); + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, is.Tell() - 1); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); } - is.Take(); } return codepoint; } @@ -933,14 +619,7 @@ private: *stack_.template Push() = c; ++length_; } - - RAPIDJSON_FORCEINLINE void* Push(SizeType count) { - length_ += count; - return stack_.template Push(count); - } - size_t Length() const { return length_; } - Ch* Pop() { return stack_.template Pop(length_); } @@ -959,9 +638,6 @@ private: internal::StreamLocalCopy copy(is); InputStream& s(copy.s); - RAPIDJSON_ASSERT(s.Peek() == '\"'); - s.Take(); // Skip '\"' - bool success = false; if (parseFlags & kParseInsituFlag) { typename InputStream::Ch *head = s.PutBegin(); @@ -969,7 +645,7 @@ private: RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; size_t length = s.PutEnd(head) - 1; RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); - const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + const typename TargetEncoding::Ch* const str = (typename TargetEncoding::Ch*)head; success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); } else { @@ -980,7 +656,7 @@ private: const typename TargetEncoding::Ch* const str = stackStream.Pop(); success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); } - if (RAPIDJSON_UNLIKELY(!success)) + if (!success) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); } @@ -991,421 +667,74 @@ private: //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN #define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 static const char escape[256] = { - Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', - Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, - 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, - 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', + Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, + 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, + 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 }; #undef Z16 //!@endcond - for (;;) { - // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation. - if (!(parseFlags & kParseValidateEncodingFlag)) - ScanCopyUnescapedString(is, os); + RAPIDJSON_ASSERT(is.Peek() == '\"'); + is.Take(); // Skip '\"' + for (;;) { Ch c = is.Peek(); - if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape - size_t escapeOffset = is.Tell(); // For invalid escaping, report the inital '\\' as error offset + if (c == '\\') { // Escape is.Take(); - Ch e = is.Peek(); - if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { - is.Take(); - os.Put(static_cast(escape[static_cast(e)])); + Ch e = is.Take(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && escape[(unsigned char)e]) { + os.Put(escape[(unsigned char)e]); } - else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode - is.Take(); - unsigned codepoint = ParseHex4(is, escapeOffset); + else if (e == 'u') { // Unicode + unsigned codepoint = ParseHex4(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDBFF)) { + if (codepoint >= 0xD800 && codepoint <= 0xDBFF) { // Handle UTF-16 surrogate pair - if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) - RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); - unsigned codepoint2 = ParseHex4(is, escapeOffset); + if (is.Take() != '\\' || is.Take() != 'u') + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); + unsigned codepoint2 = ParseHex4(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) - RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + if (codepoint2 < 0xDC00 || codepoint2 > 0xDFFF) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; } TEncoding::Encode(os, codepoint); } else - RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); } - else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote + else if (c == '"') { // Closing double quote is.Take(); os.Put('\0'); // null-terminate the string return; } - else if (RAPIDJSON_UNLIKELY(static_cast(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF - if (c == '\0') - RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); - else + else if (c == '\0') + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell() - 1); + else if ((unsigned)c < 0x20) // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); + else { + if (parseFlags & kParseValidateEncodingFlag ? + !Transcoder::Validate(is, os) : + !Transcoder::Transcode(is, os)) RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); } - else { - size_t offset = is.Tell(); - if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? - !Transcoder::Validate(is, os) : - !Transcoder::Transcode(is, os)))) - RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); - } } } - template - static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) { - // Do nothing for generic version - } - -#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) - // StringStream -> StackStream - static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { - const char* p = is.src_; - - // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - while (p != nextAligned) - if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { - is.src_ = p; - return; - } - else - os.Put(*p++); - - // The rest of string using SIMD - static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; - static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; - const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); - const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); - const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); - - for (;; p += 16) { - const __m128i s = _mm_load_si128(reinterpret_cast(p)); - const __m128i t1 = _mm_cmpeq_epi8(s, dq); - const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F - const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); - unsigned short r = static_cast(_mm_movemask_epi8(x)); - if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped - SizeType length; - #ifdef _MSC_VER // Find the index of first escaped - unsigned long offset; - _BitScanForward(&offset, r); - length = offset; - #else - length = static_cast(__builtin_ffs(r) - 1); - #endif - if (length != 0) { - char* q = reinterpret_cast(os.Push(length)); - for (size_t i = 0; i < length; i++) - q[i] = p[i]; - - p += length; - } - break; - } - _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); - } - - is.src_ = p; - } - - // InsituStringStream -> InsituStringStream - static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { - RAPIDJSON_ASSERT(&is == &os); - (void)os; - - if (is.src_ == is.dst_) { - SkipUnescapedString(is); - return; - } - - char* p = is.src_; - char *q = is.dst_; - - // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - while (p != nextAligned) - if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { - is.src_ = p; - is.dst_ = q; - return; - } - else - *q++ = *p++; - - // The rest of string using SIMD - static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; - static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; - const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); - const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); - const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); - - for (;; p += 16, q += 16) { - const __m128i s = _mm_load_si128(reinterpret_cast(p)); - const __m128i t1 = _mm_cmpeq_epi8(s, dq); - const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F - const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); - unsigned short r = static_cast(_mm_movemask_epi8(x)); - if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped - size_t length; -#ifdef _MSC_VER // Find the index of first escaped - unsigned long offset; - _BitScanForward(&offset, r); - length = offset; -#else - length = static_cast(__builtin_ffs(r) - 1); -#endif - for (const char* pend = p + length; p != pend; ) - *q++ = *p++; - break; - } - _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s); - } - - is.src_ = p; - is.dst_ = q; - } - - // When read/write pointers are the same for insitu stream, just skip unescaped characters - static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { - RAPIDJSON_ASSERT(is.src_ == is.dst_); - char* p = is.src_; - - // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - for (; p != nextAligned; p++) - if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { - is.src_ = is.dst_ = p; - return; - } - - // The rest of string using SIMD - static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; - static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; - const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); - const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); - const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); - - for (;; p += 16) { - const __m128i s = _mm_load_si128(reinterpret_cast(p)); - const __m128i t1 = _mm_cmpeq_epi8(s, dq); - const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F - const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); - unsigned short r = static_cast(_mm_movemask_epi8(x)); - if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped - size_t length; -#ifdef _MSC_VER // Find the index of first escaped - unsigned long offset; - _BitScanForward(&offset, r); - length = offset; -#else - length = static_cast(__builtin_ffs(r) - 1); -#endif - p += length; - break; - } - } - - is.src_ = is.dst_ = p; - } -#elif defined(RAPIDJSON_NEON) - // StringStream -> StackStream - static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { - const char* p = is.src_; - - // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - while (p != nextAligned) - if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { - is.src_ = p; - return; - } - else - os.Put(*p++); - - // The rest of string using SIMD - const uint8x16_t s0 = vmovq_n_u8('"'); - const uint8x16_t s1 = vmovq_n_u8('\\'); - const uint8x16_t s2 = vmovq_n_u8('\b'); - const uint8x16_t s3 = vmovq_n_u8(32); - - for (;; p += 16) { - const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); - uint8x16_t x = vceqq_u8(s, s0); - x = vorrq_u8(x, vceqq_u8(s, s1)); - x = vorrq_u8(x, vceqq_u8(s, s2)); - x = vorrq_u8(x, vcltq_u8(s, s3)); - - x = vrev64q_u8(x); // Rev in 64 - uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract - uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract - - SizeType length = 0; - bool escaped = false; - if (low == 0) { - if (high != 0) { - unsigned lz = (unsigned)__builtin_clzll(high);; - length = 8 + (lz >> 3); - escaped = true; - } - } else { - unsigned lz = (unsigned)__builtin_clzll(low);; - length = lz >> 3; - escaped = true; - } - if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped - if (length != 0) { - char* q = reinterpret_cast(os.Push(length)); - for (size_t i = 0; i < length; i++) - q[i] = p[i]; - - p += length; - } - break; - } - vst1q_u8(reinterpret_cast(os.Push(16)), s); - } - - is.src_ = p; - } - - // InsituStringStream -> InsituStringStream - static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { - RAPIDJSON_ASSERT(&is == &os); - (void)os; - - if (is.src_ == is.dst_) { - SkipUnescapedString(is); - return; - } - - char* p = is.src_; - char *q = is.dst_; - - // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - while (p != nextAligned) - if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { - is.src_ = p; - is.dst_ = q; - return; - } - else - *q++ = *p++; - - // The rest of string using SIMD - const uint8x16_t s0 = vmovq_n_u8('"'); - const uint8x16_t s1 = vmovq_n_u8('\\'); - const uint8x16_t s2 = vmovq_n_u8('\b'); - const uint8x16_t s3 = vmovq_n_u8(32); - - for (;; p += 16, q += 16) { - const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); - uint8x16_t x = vceqq_u8(s, s0); - x = vorrq_u8(x, vceqq_u8(s, s1)); - x = vorrq_u8(x, vceqq_u8(s, s2)); - x = vorrq_u8(x, vcltq_u8(s, s3)); - - x = vrev64q_u8(x); // Rev in 64 - uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract - uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract - - SizeType length = 0; - bool escaped = false; - if (low == 0) { - if (high != 0) { - unsigned lz = (unsigned)__builtin_clzll(high); - length = 8 + (lz >> 3); - escaped = true; - } - } else { - unsigned lz = (unsigned)__builtin_clzll(low); - length = lz >> 3; - escaped = true; - } - if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped - for (const char* pend = p + length; p != pend; ) { - *q++ = *p++; - } - break; - } - vst1q_u8(reinterpret_cast(q), s); - } - - is.src_ = p; - is.dst_ = q; - } - - // When read/write pointers are the same for insitu stream, just skip unescaped characters - static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { - RAPIDJSON_ASSERT(is.src_ == is.dst_); - char* p = is.src_; - - // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - for (; p != nextAligned; p++) - if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { - is.src_ = is.dst_ = p; - return; - } - - // The rest of string using SIMD - const uint8x16_t s0 = vmovq_n_u8('"'); - const uint8x16_t s1 = vmovq_n_u8('\\'); - const uint8x16_t s2 = vmovq_n_u8('\b'); - const uint8x16_t s3 = vmovq_n_u8(32); - - for (;; p += 16) { - const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); - uint8x16_t x = vceqq_u8(s, s0); - x = vorrq_u8(x, vceqq_u8(s, s1)); - x = vorrq_u8(x, vceqq_u8(s, s2)); - x = vorrq_u8(x, vcltq_u8(s, s3)); - - x = vrev64q_u8(x); // Rev in 64 - uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract - uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract - - if (low == 0) { - if (high != 0) { - int lz = __builtin_clzll(high); - p += 8 + (lz >> 3); - break; - } - } else { - int lz = __builtin_clzll(low); - p += lz >> 3; - break; - } - } - - is.src_ = is.dst_ = p; - } -#endif // RAPIDJSON_NEON - - template + template class NumberStream; template - class NumberStream { + class NumberStream { public: - typedef typename InputStream::Ch Ch; - NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } + ~NumberStream() {} RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } - RAPIDJSON_FORCEINLINE void Push(char) {} - size_t Tell() { return is.Tell(); } size_t Length() { return 0; } const char* Pop() { return 0; } @@ -1417,20 +746,17 @@ private: }; template - class NumberStream : public NumberStream { - typedef NumberStream Base; + class NumberStream : public NumberStream { + typedef NumberStream Base; public: - NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {} + NumberStream(GenericReader& reader, InputStream& is) : NumberStream(reader, is), stackStream(reader.stack_) {} + ~NumberStream() {} RAPIDJSON_FORCEINLINE Ch TakePush() { - stackStream.Put(static_cast(Base::is.Peek())); + stackStream.Put((char)Base::is.Peek()); return Base::is.Take(); } - RAPIDJSON_FORCEINLINE void Push(char c) { - stackStream.Put(c); - } - size_t Length() { return stackStream.Length(); } const char* Pop() { @@ -1442,48 +768,34 @@ private: StackStream stackStream; }; - template - class NumberStream : public NumberStream { - typedef NumberStream Base; - public: - NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {} - - RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } - }; - template void ParseNumber(InputStream& is, Handler& handler) { internal::StreamLocalCopy copy(is); - NumberStream s(*this, copy.s); - - size_t startOffset = s.Tell(); - double d = 0.0; - bool useNanOrInf = false; + NumberStream s(*this, copy.s); // Parse minus - bool minus = Consume(s, '-'); + bool minus = false; + if (s.Peek() == '-') { + minus = true; + s.Take(); + } // Parse int: zero / ( digit1-9 *DIGIT ) unsigned i = 0; uint64_t i64 = 0; bool use64bit = false; int significandDigit = 0; - if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) { + if (s.Peek() == '0') { i = 0; s.TakePush(); } - else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) { + else if (s.Peek() >= '1' && s.Peek() <= '9') { i = static_cast(s.TakePush() - '0'); if (minus) - while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648 - if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) { + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i >= 214748364) { // 2^31 = 2147483648 + if (i != 214748364 || s.Peek() > '8') { i64 = i; use64bit = true; break; @@ -1493,9 +805,9 @@ private: significandDigit++; } else - while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295 - if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) { + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i >= 429496729) { // 2^32 - 1 = 4294967295 + if (i != 429496729 || s.Peek() > '5') { i64 = i; use64bit = true; break; @@ -1505,41 +817,18 @@ private: significandDigit++; } } - // Parse NaN or Infinity here - else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { - if (Consume(s, 'N')) { - if (Consume(s, 'a') && Consume(s, 'N')) { - d = std::numeric_limits::quiet_NaN(); - useNanOrInf = true; - } - } - else if (RAPIDJSON_LIKELY(Consume(s, 'I'))) { - if (Consume(s, 'n') && Consume(s, 'f')) { - d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); - useNanOrInf = true; - - if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') - && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) { - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); - } - } - } - - if (RAPIDJSON_UNLIKELY(!useNanOrInf)) { - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); - } - } else RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); // Parse 64bit int bool useDouble = false; + double d = 0.0; if (use64bit) { - if (minus) - while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808 - if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) { - d = static_cast(i64); + if (minus) + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC)) // 2^63 = 9223372036854775808 + if (i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8') { + d = i64; useDouble = true; break; } @@ -1547,10 +836,10 @@ private: significandDigit++; } else - while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615 - if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) { - d = static_cast(i64); + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999)) // 2^64 - 1 = 18446744073709551615 + if (i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5') { + d = i64; useDouble = true; break; } @@ -1561,9 +850,9 @@ private: // Force double for big integer if (useDouble) { - while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - if (RAPIDJSON_UNLIKELY(d >= 1.7976931348623157e307)) // DBL_MAX / 10.0 - RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (d >= 1.7976931348623157e307) // DBL_MAX / 10.0 + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); d = d * 10 + (s.TakePush() - '0'); } } @@ -1571,10 +860,11 @@ private: // Parse frac = decimal-point 1*DIGIT int expFrac = 0; size_t decimalPosition; - if (Consume(s, '.')) { + if (s.Peek() == '.') { + s.Take(); decimalPosition = s.Length(); - if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) + if (!(s.Peek() >= '0' && s.Peek() <= '9')) RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); if (!useDouble) { @@ -1582,8 +872,8 @@ private: // Use i64 to store significand in 64-bit architecture if (!use64bit) i64 = i; - - while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + + while (s.Peek() >= '0' && s.Peek() <= '9') { if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path break; else { @@ -1594,19 +884,19 @@ private: } } - d = static_cast(i64); + d = (double)i64; #else // Use double to store significand in 32-bit architecture - d = static_cast(use64bit ? i64 : i); + d = use64bit ? (double)i64 : (double)i; #endif useDouble = true; } - while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + while (s.Peek() >= '0' && s.Peek() <= '9') { if (significandDigit < 17) { d = d * 10.0 + (s.TakePush() - '0'); --expFrac; - if (RAPIDJSON_LIKELY(d > 0.0)) + if (d > 0.0) significandDigit++; } else @@ -1618,35 +908,38 @@ private: // Parse exp = e [ minus / plus ] 1*DIGIT int exp = 0; - if (Consume(s, 'e') || Consume(s, 'E')) { + if (s.Peek() == 'e' || s.Peek() == 'E') { if (!useDouble) { - d = static_cast(use64bit ? i64 : i); + d = use64bit ? i64 : i; useDouble = true; } + s.Take(); bool expMinus = false; - if (Consume(s, '+')) - ; - else if (Consume(s, '-')) + if (s.Peek() == '+') + s.Take(); + else if (s.Peek() == '-') { + s.Take(); expMinus = true; + } - if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - exp = static_cast(s.Take() - '0'); + if (s.Peek() >= '0' && s.Peek() <= '9') { + exp = s.Take() - '0'; if (expMinus) { - while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - exp = exp * 10 + static_cast(s.Take() - '0'); + while (s.Peek() >= '0' && s.Peek() <= '9') { + exp = exp * 10 + (s.Take() - '0'); if (exp >= 214748364) { // Issue #313: prevent overflow exponent - while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent + while (s.Peek() >= '0' && s.Peek() <= '9') // Consume the rest of exponent s.Take(); } } } else { // positive exp int maxExp = 308 - expFrac; - while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - exp = exp * 10 + static_cast(s.Take() - '0'); - if (RAPIDJSON_UNLIKELY(exp > maxExp)) - RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + while (s.Peek() >= '0' && s.Peek() <= '9') { + exp = exp * 10 + (s.Take() - '0'); + if (exp > maxExp) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); } } } @@ -1659,63 +952,34 @@ private: // Finish parsing, call event according to the type of number. bool cont = true; + size_t length = s.Length(); + const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. - if (parseFlags & kParseNumbersAsStringsFlag) { - if (parseFlags & kParseInsituFlag) { - s.Pop(); // Pop stack no matter if it will be used or not. - typename InputStream::Ch* head = is.PutBegin(); - const size_t length = s.Tell() - startOffset; - RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); - // unable to insert the \0 character here, it will erase the comma after this number - const typename TargetEncoding::Ch* const str = reinterpret_cast(head); - cont = handler.RawNumber(str, SizeType(length), false); - } - else { - SizeType numCharsToCopy = static_cast(s.Length()); - StringStream srcStream(s.Pop()); - StackStream dstStream(stack_); - while (numCharsToCopy--) { - Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); - } - dstStream.Put('\0'); - const typename TargetEncoding::Ch* str = dstStream.Pop(); - const SizeType length = static_cast(dstStream.Length()) - 1; - cont = handler.RawNumber(str, SizeType(length), true); - } + if (useDouble) { + int p = exp + expFrac; + if (parseFlags & kParseFullPrecisionFlag) + d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); + else + d = internal::StrtodNormalPrecision(d, p); + + cont = handler.Double(minus ? -d : d); } else { - size_t length = s.Length(); - const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. - - if (useDouble) { - int p = exp + expFrac; - if (parseFlags & kParseFullPrecisionFlag) - d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); - else - d = internal::StrtodNormalPrecision(d, p); - - cont = handler.Double(minus ? -d : d); - } - else if (useNanOrInf) { - cont = handler.Double(d); - } - else { - if (use64bit) { - if (minus) - cont = handler.Int64(static_cast(~i64 + 1)); - else - cont = handler.Uint64(i64); - } - else { - if (minus) - cont = handler.Int(static_cast(~i + 1)); - else - cont = handler.Uint(i); - } - } + if (use64bit) { + if (minus) + cont = handler.Int64(-(int64_t)i64); + else + cont = handler.Uint64(i64); + } + else { + if (minus) + cont = handler.Int(-(int)i); + else + cont = handler.Uint(i); + } } - if (RAPIDJSON_UNLIKELY(!cont)) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); + if (!cont) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); } // Parse any JSON value @@ -1728,10 +992,7 @@ private: case '"': ParseString(is, handler); break; case '{': ParseObject(is, handler); break; case '[': ParseArray (is, handler); break; - default : - ParseNumber(is, handler); - break; - + default : ParseNumber(is, handler); } } @@ -1739,29 +1000,27 @@ private: // States enum IterativeParsingState { - IterativeParsingFinishState = 0, // sink states at top - IterativeParsingErrorState, // sink states at top - IterativeParsingStartState, + IterativeParsingStartState = 0, + IterativeParsingFinishState, + IterativeParsingErrorState, // Object states IterativeParsingObjectInitialState, IterativeParsingMemberKeyState, + IterativeParsingKeyValueDelimiterState, IterativeParsingMemberValueState, + IterativeParsingMemberDelimiterState, IterativeParsingObjectFinishState, // Array states IterativeParsingArrayInitialState, IterativeParsingElementState, + IterativeParsingElementDelimiterState, IterativeParsingArrayFinishState, // Single value state IterativeParsingValueState, - - // Delimiter states (at bottom) - IterativeParsingElementDelimiterState, - IterativeParsingMemberDelimiterState, - IterativeParsingKeyValueDelimiterState, - + cIterativeParsingStateCount }; @@ -1805,9 +1064,9 @@ private: #undef N #undef N16 //!@endcond - - if (sizeof(Ch) == 1 || static_cast(c) < 256) - return static_cast(tokenMap[static_cast(c)]); + + if (sizeof(Ch) == 1 || unsigned(c) < 256) + return (Token)tokenMap[(unsigned char)c]; else return NumberToken; } @@ -1815,18 +1074,6 @@ private: RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) { // current state x one lookahead token -> new state static const char G[cIterativeParsingStateCount][kTokenCount] = { - // Finish(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, - // Error(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, // Start { IterativeParsingArrayInitialState, // Left bracket @@ -1841,6 +1088,18 @@ private: IterativeParsingValueState, // Null IterativeParsingValueState // Number }, + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, // ObjectInitial { IterativeParsingErrorState, // Left bracket @@ -1869,6 +1128,20 @@ private: IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, + // KeyValueDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, // MemberValue { IterativeParsingErrorState, // Left bracket @@ -1883,6 +1156,20 @@ private: IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, + // MemberDelimiter + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, // ObjectFinish(sink state) { IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, @@ -1917,6 +1204,20 @@ private: IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, + // ElementDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, // ArrayFinish(sink state) { IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, @@ -1928,52 +1229,10 @@ private: IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState - }, - // ElementDelimiter - { - IterativeParsingArrayInitialState, // Left bracket(push Element state) - IterativeParsingArrayFinishState, // Right bracket - IterativeParsingObjectInitialState, // Left curly bracket(push Element state) - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingElementState, // String - IterativeParsingElementState, // False - IterativeParsingElementState, // True - IterativeParsingElementState, // Null - IterativeParsingElementState // Number - }, - // MemberDelimiter - { - IterativeParsingErrorState, // Left bracket - IterativeParsingErrorState, // Right bracket - IterativeParsingErrorState, // Left curly bracket - IterativeParsingObjectFinishState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberKeyState, // String - IterativeParsingErrorState, // False - IterativeParsingErrorState, // True - IterativeParsingErrorState, // Null - IterativeParsingErrorState // Number - }, - // KeyValueDelimiter - { - IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) - IterativeParsingErrorState, // Right bracket - IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberValueState, // String - IterativeParsingMemberValueState, // False - IterativeParsingMemberValueState, // True - IterativeParsingMemberValueState, // Null - IterativeParsingMemberValueState // Number - }, + } }; // End of G - return static_cast(G[state][token]); + return (IterativeParsingState)G[state][token]; } // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). @@ -2050,11 +1309,6 @@ private: case IterativeParsingObjectFinishState: { - // Transit from delimiter is only allowed when trailing commas are enabled - if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell()); - return IterativeParsingErrorState; - } // Get member count. SizeType c = *stack_.template Pop(1); // If the object is not empty, count the last member. @@ -2080,11 +1334,6 @@ private: case IterativeParsingArrayFinishState: { - // Transit from delimiter is only allowed when trailing commas are enabled - if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell()); - return IterativeParsingErrorState; - } // Get element count. SizeType c = *stack_.template Pop(1); // If the array is not empty, count the last element. @@ -2136,68 +1385,55 @@ private: // Error flag has been set. return; } - + switch (src) { - case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return; - case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return; + case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); + case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); case IterativeParsingObjectInitialState: - case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return; - case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return; - case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return; - case IterativeParsingKeyValueDelimiterState: - case IterativeParsingArrayInitialState: - case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return; - default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return; - } + case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); + case IterativeParsingElementState: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + default: RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + } } - RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) { - return s >= IterativeParsingElementDelimiterState; - } - - RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) { - return s <= IterativeParsingErrorState; - } - template ParseResult IterativeParse(InputStream& is, Handler& handler) { parseResult_.Clear(); ClearStackOnExit scope(*this); IterativeParsingState state = IterativeParsingStartState; - - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + SkipWhitespace(is); while (is.Peek() != '\0') { Token t = Tokenize(is.Peek()); IterativeParsingState n = Predict(state, t); IterativeParsingState d = Transit(state, t, n, is, handler); - + if (d == IterativeParsingErrorState) { HandleError(state, is); break; } - + state = d; - + // Do not further consume streams if a root JSON has been parsed. if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) break; - - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + SkipWhitespace(is); } - + // Handle the end of file. if (state != IterativeParsingFinishState) HandleError(state, is); - + return parseResult_; } static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. ParseResult parseResult_; - IterativeParsingState state_; }; // class GenericReader //! Reader with UTF8 encoding and default allocator. @@ -2205,11 +1441,6 @@ typedef GenericReader, UTF8<> > Reader; RAPIDJSON_NAMESPACE_END -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - - #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif diff --git a/contrib/rapidjson/include/rapidjson/schema.h b/contrib/rapidjson/include/rapidjson/schema.h deleted file mode 100644 index abcf1a102..000000000 --- a/contrib/rapidjson/include/rapidjson/schema.h +++ /dev/null @@ -1,2016 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available-> -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License-> You may obtain a copy of the License at -// -// http://opensource->org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied-> See the License for the -// specific language governing permissions and limitations under the License-> - -#ifndef RAPIDJSON_SCHEMA_H_ -#define RAPIDJSON_SCHEMA_H_ - -#include "document.h" -#include "pointer.h" -#include // abs, floor - -#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) -#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1 -#else -#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0 -#endif - -#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) -#define RAPIDJSON_SCHEMA_USE_STDREGEX 1 -#else -#define RAPIDJSON_SCHEMA_USE_STDREGEX 0 -#endif - -#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX -#include "internal/regex.h" -#elif RAPIDJSON_SCHEMA_USE_STDREGEX -#include -#endif - -#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX -#define RAPIDJSON_SCHEMA_HAS_REGEX 1 -#else -#define RAPIDJSON_SCHEMA_HAS_REGEX 0 -#endif - -#ifndef RAPIDJSON_SCHEMA_VERBOSE -#define RAPIDJSON_SCHEMA_VERBOSE 0 -#endif - -#if RAPIDJSON_SCHEMA_VERBOSE -#include "stringbuffer.h" -#endif - -RAPIDJSON_DIAG_PUSH - -#if defined(__GNUC__) -RAPIDJSON_DIAG_OFF(effc++) -#endif - -#ifdef __clang__ -RAPIDJSON_DIAG_OFF(weak-vtables) -RAPIDJSON_DIAG_OFF(exit-time-destructors) -RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) -RAPIDJSON_DIAG_OFF(variadic-macros) -#endif - -#ifdef _MSC_VER -RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -/////////////////////////////////////////////////////////////////////////////// -// Verbose Utilities - -#if RAPIDJSON_SCHEMA_VERBOSE - -namespace internal { - -inline void PrintInvalidKeyword(const char* keyword) { - printf("Fail keyword: %s\n", keyword); -} - -inline void PrintInvalidKeyword(const wchar_t* keyword) { - wprintf(L"Fail keyword: %ls\n", keyword); -} - -inline void PrintInvalidDocument(const char* document) { - printf("Fail document: %s\n\n", document); -} - -inline void PrintInvalidDocument(const wchar_t* document) { - wprintf(L"Fail document: %ls\n\n", document); -} - -inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) { - printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d); -} - -inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) { - wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d); -} - -} // namespace internal - -#endif // RAPIDJSON_SCHEMA_VERBOSE - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_INVALID_KEYWORD_RETURN - -#if RAPIDJSON_SCHEMA_VERBOSE -#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword) -#else -#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) -#endif - -#define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\ -RAPIDJSON_MULTILINEMACRO_BEGIN\ - context.invalidKeyword = keyword.GetString();\ - RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\ - return false;\ -RAPIDJSON_MULTILINEMACRO_END - -/////////////////////////////////////////////////////////////////////////////// -// Forward declarations - -template -class GenericSchemaDocument; - -namespace internal { - -template -class Schema; - -/////////////////////////////////////////////////////////////////////////////// -// ISchemaValidator - -class ISchemaValidator { -public: - virtual ~ISchemaValidator() {} - virtual bool IsValid() const = 0; -}; - -/////////////////////////////////////////////////////////////////////////////// -// ISchemaStateFactory - -template -class ISchemaStateFactory { -public: - virtual ~ISchemaStateFactory() {} - virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0; - virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; - virtual void* CreateHasher() = 0; - virtual uint64_t GetHashCode(void* hasher) = 0; - virtual void DestroryHasher(void* hasher) = 0; - virtual void* MallocState(size_t size) = 0; - virtual void FreeState(void* p) = 0; -}; - -/////////////////////////////////////////////////////////////////////////////// -// Hasher - -// For comparison of compound value -template -class Hasher { -public: - typedef typename Encoding::Ch Ch; - - Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {} - - bool Null() { return WriteType(kNullType); } - bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); } - bool Int(int i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } - bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } - bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } - bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } - bool Double(double d) { - Number n; - if (d < 0) n.u.i = static_cast(d); - else n.u.u = static_cast(d); - n.d = d; - return WriteNumber(n); - } - - bool RawNumber(const Ch* str, SizeType len, bool) { - WriteBuffer(kNumberType, str, len * sizeof(Ch)); - return true; - } - - bool String(const Ch* str, SizeType len, bool) { - WriteBuffer(kStringType, str, len * sizeof(Ch)); - return true; - } - - bool StartObject() { return true; } - bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } - bool EndObject(SizeType memberCount) { - uint64_t h = Hash(0, kObjectType); - uint64_t* kv = stack_.template Pop(memberCount * 2); - for (SizeType i = 0; i < memberCount; i++) - h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive - *stack_.template Push() = h; - return true; - } - - bool StartArray() { return true; } - bool EndArray(SizeType elementCount) { - uint64_t h = Hash(0, kArrayType); - uint64_t* e = stack_.template Pop(elementCount); - for (SizeType i = 0; i < elementCount; i++) - h = Hash(h, e[i]); // Use hash to achieve element order sensitive - *stack_.template Push() = h; - return true; - } - - bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); } - - uint64_t GetHashCode() const { - RAPIDJSON_ASSERT(IsValid()); - return *stack_.template Top(); - } - -private: - static const size_t kDefaultSize = 256; - struct Number { - union U { - uint64_t u; - int64_t i; - }u; - double d; - }; - - bool WriteType(Type type) { return WriteBuffer(type, 0, 0); } - - bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); } - - bool WriteBuffer(Type type, const void* data, size_t len) { - // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/ - uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type); - const unsigned char* d = static_cast(data); - for (size_t i = 0; i < len; i++) - h = Hash(h, d[i]); - *stack_.template Push() = h; - return true; - } - - static uint64_t Hash(uint64_t h, uint64_t d) { - static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3); - h ^= d; - h *= kPrime; - return h; - } - - Stack stack_; -}; - -/////////////////////////////////////////////////////////////////////////////// -// SchemaValidationContext - -template -struct SchemaValidationContext { - typedef Schema SchemaType; - typedef ISchemaStateFactory SchemaValidatorFactoryType; - typedef typename SchemaType::ValueType ValueType; - typedef typename ValueType::Ch Ch; - - enum PatternValidatorType { - kPatternValidatorOnly, - kPatternValidatorWithProperty, - kPatternValidatorWithAdditionalProperty - }; - - SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) : - factory(f), - schema(s), - valueSchema(), - invalidKeyword(), - hasher(), - arrayElementHashCodes(), - validators(), - validatorCount(), - patternPropertiesValidators(), - patternPropertiesValidatorCount(), - patternPropertiesSchemas(), - patternPropertiesSchemaCount(), - valuePatternValidatorType(kPatternValidatorOnly), - propertyExist(), - inArray(false), - valueUniqueness(false), - arrayUniqueness(false) - { - } - - ~SchemaValidationContext() { - if (hasher) - factory.DestroryHasher(hasher); - if (validators) { - for (SizeType i = 0; i < validatorCount; i++) - factory.DestroySchemaValidator(validators[i]); - factory.FreeState(validators); - } - if (patternPropertiesValidators) { - for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) - factory.DestroySchemaValidator(patternPropertiesValidators[i]); - factory.FreeState(patternPropertiesValidators); - } - if (patternPropertiesSchemas) - factory.FreeState(patternPropertiesSchemas); - if (propertyExist) - factory.FreeState(propertyExist); - } - - SchemaValidatorFactoryType& factory; - const SchemaType* schema; - const SchemaType* valueSchema; - const Ch* invalidKeyword; - void* hasher; // Only validator access - void* arrayElementHashCodes; // Only validator access this - ISchemaValidator** validators; - SizeType validatorCount; - ISchemaValidator** patternPropertiesValidators; - SizeType patternPropertiesValidatorCount; - const SchemaType** patternPropertiesSchemas; - SizeType patternPropertiesSchemaCount; - PatternValidatorType valuePatternValidatorType; - PatternValidatorType objectPatternValidatorType; - SizeType arrayElementIndex; - bool* propertyExist; - bool inArray; - bool valueUniqueness; - bool arrayUniqueness; -}; - -/////////////////////////////////////////////////////////////////////////////// -// Schema - -template -class Schema { -public: - typedef typename SchemaDocumentType::ValueType ValueType; - typedef typename SchemaDocumentType::AllocatorType AllocatorType; - typedef typename SchemaDocumentType::PointerType PointerType; - typedef typename ValueType::EncodingType EncodingType; - typedef typename EncodingType::Ch Ch; - typedef SchemaValidationContext Context; - typedef Schema SchemaType; - typedef GenericValue SValue; - friend class GenericSchemaDocument; - - Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : - allocator_(allocator), - typeless_(schemaDocument->GetTypeless()), - enum_(), - enumCount_(), - not_(), - type_((1 << kTotalSchemaType) - 1), // typeless - validatorCount_(), - properties_(), - additionalPropertiesSchema_(), - patternProperties_(), - patternPropertyCount_(), - propertyCount_(), - minProperties_(), - maxProperties_(SizeType(~0)), - additionalProperties_(true), - hasDependencies_(), - hasRequired_(), - hasSchemaDependencies_(), - additionalItemsSchema_(), - itemsList_(), - itemsTuple_(), - itemsTupleCount_(), - minItems_(), - maxItems_(SizeType(~0)), - additionalItems_(true), - uniqueItems_(false), - pattern_(), - minLength_(0), - maxLength_(~SizeType(0)), - exclusiveMinimum_(false), - exclusiveMaximum_(false) - { - typedef typename SchemaDocumentType::ValueType ValueType; - typedef typename ValueType::ConstValueIterator ConstValueIterator; - typedef typename ValueType::ConstMemberIterator ConstMemberIterator; - - if (!value.IsObject()) - return; - - if (const ValueType* v = GetMember(value, GetTypeString())) { - type_ = 0; - if (v->IsString()) - AddType(*v); - else if (v->IsArray()) - for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) - AddType(*itr); - } - - if (const ValueType* v = GetMember(value, GetEnumString())) - if (v->IsArray() && v->Size() > 0) { - enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); - for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { - typedef Hasher > EnumHasherType; - char buffer[256 + 24]; - MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer)); - EnumHasherType h(&hasherAllocator, 256); - itr->Accept(h); - enum_[enumCount_++] = h.GetHashCode(); - } - } - - if (schemaDocument) { - AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); - AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); - AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); - } - - if (const ValueType* v = GetMember(value, GetNotString())) { - schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document); - notValidatorIndex_ = validatorCount_; - validatorCount_++; - } - - // Object - - const ValueType* properties = GetMember(value, GetPropertiesString()); - const ValueType* required = GetMember(value, GetRequiredString()); - const ValueType* dependencies = GetMember(value, GetDependenciesString()); - { - // Gather properties from properties/required/dependencies - SValue allProperties(kArrayType); - - if (properties && properties->IsObject()) - for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) - AddUniqueElement(allProperties, itr->name); - - if (required && required->IsArray()) - for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) - if (itr->IsString()) - AddUniqueElement(allProperties, *itr); - - if (dependencies && dependencies->IsObject()) - for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { - AddUniqueElement(allProperties, itr->name); - if (itr->value.IsArray()) - for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i) - if (i->IsString()) - AddUniqueElement(allProperties, *i); - } - - if (allProperties.Size() > 0) { - propertyCount_ = allProperties.Size(); - properties_ = static_cast(allocator_->Malloc(sizeof(Property) * propertyCount_)); - for (SizeType i = 0; i < propertyCount_; i++) { - new (&properties_[i]) Property(); - properties_[i].name = allProperties[i]; - properties_[i].schema = typeless_; - } - } - } - - if (properties && properties->IsObject()) { - PointerType q = p.Append(GetPropertiesString(), allocator_); - for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { - SizeType index; - if (FindPropertyIndex(itr->name, &index)) - schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document); - } - } - - if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { - PointerType q = p.Append(GetPatternPropertiesString(), allocator_); - patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); - patternPropertyCount_ = 0; - - for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { - new (&patternProperties_[patternPropertyCount_]) PatternProperty(); - patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name); - schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document); - patternPropertyCount_++; - } - } - - if (required && required->IsArray()) - for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) - if (itr->IsString()) { - SizeType index; - if (FindPropertyIndex(*itr, &index)) { - properties_[index].required = true; - hasRequired_ = true; - } - } - - if (dependencies && dependencies->IsObject()) { - PointerType q = p.Append(GetDependenciesString(), allocator_); - hasDependencies_ = true; - for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { - SizeType sourceIndex; - if (FindPropertyIndex(itr->name, &sourceIndex)) { - if (itr->value.IsArray()) { - properties_[sourceIndex].dependencies = static_cast(allocator_->Malloc(sizeof(bool) * propertyCount_)); - std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_); - for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) { - SizeType targetIndex; - if (FindPropertyIndex(*targetItr, &targetIndex)) - properties_[sourceIndex].dependencies[targetIndex] = true; - } - } - else if (itr->value.IsObject()) { - hasSchemaDependencies_ = true; - schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document); - properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; - validatorCount_++; - } - } - } - } - - if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) { - if (v->IsBool()) - additionalProperties_ = v->GetBool(); - else if (v->IsObject()) - schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document); - } - - AssignIfExist(minProperties_, value, GetMinPropertiesString()); - AssignIfExist(maxProperties_, value, GetMaxPropertiesString()); - - // Array - if (const ValueType* v = GetMember(value, GetItemsString())) { - PointerType q = p.Append(GetItemsString(), allocator_); - if (v->IsObject()) // List validation - schemaDocument->CreateSchema(&itemsList_, q, *v, document); - else if (v->IsArray()) { // Tuple validation - itemsTuple_ = static_cast(allocator_->Malloc(sizeof(const Schema*) * v->Size())); - SizeType index = 0; - for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++) - schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document); - } - } - - AssignIfExist(minItems_, value, GetMinItemsString()); - AssignIfExist(maxItems_, value, GetMaxItemsString()); - - if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { - if (v->IsBool()) - additionalItems_ = v->GetBool(); - else if (v->IsObject()) - schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document); - } - - AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); - - // String - AssignIfExist(minLength_, value, GetMinLengthString()); - AssignIfExist(maxLength_, value, GetMaxLengthString()); - - if (const ValueType* v = GetMember(value, GetPatternString())) - pattern_ = CreatePattern(*v); - - // Number - if (const ValueType* v = GetMember(value, GetMinimumString())) - if (v->IsNumber()) - minimum_.CopyFrom(*v, *allocator_); - - if (const ValueType* v = GetMember(value, GetMaximumString())) - if (v->IsNumber()) - maximum_.CopyFrom(*v, *allocator_); - - AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString()); - AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString()); - - if (const ValueType* v = GetMember(value, GetMultipleOfString())) - if (v->IsNumber() && v->GetDouble() > 0.0) - multipleOf_.CopyFrom(*v, *allocator_); - } - - ~Schema() { - AllocatorType::Free(enum_); - if (properties_) { - for (SizeType i = 0; i < propertyCount_; i++) - properties_[i].~Property(); - AllocatorType::Free(properties_); - } - if (patternProperties_) { - for (SizeType i = 0; i < patternPropertyCount_; i++) - patternProperties_[i].~PatternProperty(); - AllocatorType::Free(patternProperties_); - } - AllocatorType::Free(itemsTuple_); -#if RAPIDJSON_SCHEMA_HAS_REGEX - if (pattern_) { - pattern_->~RegexType(); - AllocatorType::Free(pattern_); - } -#endif - } - - bool BeginValue(Context& context) const { - if (context.inArray) { - if (uniqueItems_) - context.valueUniqueness = true; - - if (itemsList_) - context.valueSchema = itemsList_; - else if (itemsTuple_) { - if (context.arrayElementIndex < itemsTupleCount_) - context.valueSchema = itemsTuple_[context.arrayElementIndex]; - else if (additionalItemsSchema_) - context.valueSchema = additionalItemsSchema_; - else if (additionalItems_) - context.valueSchema = typeless_; - else - RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString()); - } - else - context.valueSchema = typeless_; - - context.arrayElementIndex++; - } - return true; - } - - RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { - if (context.patternPropertiesValidatorCount > 0) { - bool otherValid = false; - SizeType count = context.patternPropertiesValidatorCount; - if (context.objectPatternValidatorType != Context::kPatternValidatorOnly) - otherValid = context.patternPropertiesValidators[--count]->IsValid(); - - bool patternValid = true; - for (SizeType i = 0; i < count; i++) - if (!context.patternPropertiesValidators[i]->IsValid()) { - patternValid = false; - break; - } - - if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { - if (!patternValid) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); - } - else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { - if (!patternValid || !otherValid) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); - } - else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); - } - - if (enum_) { - const uint64_t h = context.factory.GetHashCode(context.hasher); - for (SizeType i = 0; i < enumCount_; i++) - if (enum_[i] == h) - goto foundEnum; - RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString()); - foundEnum:; - } - - if (allOf_.schemas) - for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) - if (!context.validators[i]->IsValid()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString()); - - if (anyOf_.schemas) { - for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) - if (context.validators[i]->IsValid()) - goto foundAny; - RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString()); - foundAny:; - } - - if (oneOf_.schemas) { - bool oneValid = false; - for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) - if (context.validators[i]->IsValid()) { - if (oneValid) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); - else - oneValid = true; - } - if (!oneValid) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); - } - - if (not_ && context.validators[notValidatorIndex_]->IsValid()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString()); - - return true; - } - - bool Null(Context& context) const { - if (!(type_ & (1 << kNullSchemaType))) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); - return CreateParallelValidator(context); - } - - bool Bool(Context& context, bool) const { - if (!(type_ & (1 << kBooleanSchemaType))) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); - return CreateParallelValidator(context); - } - - bool Int(Context& context, int i) const { - if (!CheckInt(context, i)) - return false; - return CreateParallelValidator(context); - } - - bool Uint(Context& context, unsigned u) const { - if (!CheckUint(context, u)) - return false; - return CreateParallelValidator(context); - } - - bool Int64(Context& context, int64_t i) const { - if (!CheckInt(context, i)) - return false; - return CreateParallelValidator(context); - } - - bool Uint64(Context& context, uint64_t u) const { - if (!CheckUint(context, u)) - return false; - return CreateParallelValidator(context); - } - - bool Double(Context& context, double d) const { - if (!(type_ & (1 << kNumberSchemaType))) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); - - if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) - return false; - - if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) - return false; - - if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) - return false; - - return CreateParallelValidator(context); - } - - bool String(Context& context, const Ch* str, SizeType length, bool) const { - if (!(type_ & (1 << kStringSchemaType))) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); - - if (minLength_ != 0 || maxLength_ != SizeType(~0)) { - SizeType count; - if (internal::CountStringCodePoint(str, length, &count)) { - if (count < minLength_) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString()); - if (count > maxLength_) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString()); - } - } - - if (pattern_ && !IsPatternMatch(pattern_, str, length)) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString()); - - return CreateParallelValidator(context); - } - - bool StartObject(Context& context) const { - if (!(type_ & (1 << kObjectSchemaType))) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); - - if (hasDependencies_ || hasRequired_) { - context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); - std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_); - } - - if (patternProperties_) { // pre-allocate schema array - SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType - context.patternPropertiesSchemas = static_cast(context.factory.MallocState(sizeof(const SchemaType*) * count)); - context.patternPropertiesSchemaCount = 0; - std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count); - } - - return CreateParallelValidator(context); - } - - bool Key(Context& context, const Ch* str, SizeType len, bool) const { - if (patternProperties_) { - context.patternPropertiesSchemaCount = 0; - for (SizeType i = 0; i < patternPropertyCount_; i++) - if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) { - context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; - context.valueSchema = typeless_; - } - } - - SizeType index; - if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { - if (context.patternPropertiesSchemaCount > 0) { - context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; - context.valueSchema = typeless_; - context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; - } - else - context.valueSchema = properties_[index].schema; - - if (context.propertyExist) - context.propertyExist[index] = true; - - return true; - } - - if (additionalPropertiesSchema_) { - if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) { - context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; - context.valueSchema = typeless_; - context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; - } - else - context.valueSchema = additionalPropertiesSchema_; - return true; - } - else if (additionalProperties_) { - context.valueSchema = typeless_; - return true; - } - - if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties - RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString()); - - return true; - } - - bool EndObject(Context& context, SizeType memberCount) const { - if (hasRequired_) - for (SizeType index = 0; index < propertyCount_; index++) - if (properties_[index].required) - if (!context.propertyExist[index]) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); - - if (memberCount < minProperties_) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); - - if (memberCount > maxProperties_) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString()); - - if (hasDependencies_) { - for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) - if (context.propertyExist[sourceIndex]) { - if (properties_[sourceIndex].dependencies) { - for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) - if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex]) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); - } - else if (properties_[sourceIndex].dependenciesSchema) - if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); - } - } - - return true; - } - - bool StartArray(Context& context) const { - if (!(type_ & (1 << kArraySchemaType))) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); - - context.arrayElementIndex = 0; - context.inArray = true; - - return CreateParallelValidator(context); - } - - bool EndArray(Context& context, SizeType elementCount) const { - context.inArray = false; - - if (elementCount < minItems_) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString()); - - if (elementCount > maxItems_) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString()); - - return true; - } - - // Generate functions for string literal according to Ch -#define RAPIDJSON_STRING_(name, ...) \ - static const ValueType& Get##name##String() {\ - static const Ch s[] = { __VA_ARGS__, '\0' };\ - static const ValueType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1));\ - return v;\ - } - - RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') - RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n') - RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't') - RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y') - RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g') - RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r') - RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r') - RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e') - RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm') - RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f') - RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f') - RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f') - RAPIDJSON_STRING_(Not, 'n', 'o', 't') - RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') - RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd') - RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's') - RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') - RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') - RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') - RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') - RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's') - RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's') - RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's') - RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's') - RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's') - RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h') - RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h') - RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n') - RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm') - RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm') - RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') - RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') - RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') - -#undef RAPIDJSON_STRING_ - -private: - enum SchemaValueType { - kNullSchemaType, - kBooleanSchemaType, - kObjectSchemaType, - kArraySchemaType, - kStringSchemaType, - kNumberSchemaType, - kIntegerSchemaType, - kTotalSchemaType - }; - -#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX - typedef internal::GenericRegex RegexType; -#elif RAPIDJSON_SCHEMA_USE_STDREGEX - typedef std::basic_regex RegexType; -#else - typedef char RegexType; -#endif - - struct SchemaArray { - SchemaArray() : schemas(), count() {} - ~SchemaArray() { AllocatorType::Free(schemas); } - const SchemaType** schemas; - SizeType begin; // begin index of context.validators - SizeType count; - }; - - template - void AddUniqueElement(V1& a, const V2& v) { - for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) - if (*itr == v) - return; - V1 c(v, *allocator_); - a.PushBack(c, *allocator_); - } - - static const ValueType* GetMember(const ValueType& value, const ValueType& name) { - typename ValueType::ConstMemberIterator itr = value.FindMember(name); - return itr != value.MemberEnd() ? &(itr->value) : 0; - } - - static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) { - if (const ValueType* v = GetMember(value, name)) - if (v->IsBool()) - out = v->GetBool(); - } - - static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) { - if (const ValueType* v = GetMember(value, name)) - if (v->IsUint64() && v->GetUint64() <= SizeType(~0)) - out = static_cast(v->GetUint64()); - } - - void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) { - if (const ValueType* v = GetMember(value, name)) { - if (v->IsArray() && v->Size() > 0) { - PointerType q = p.Append(name, allocator_); - out.count = v->Size(); - out.schemas = static_cast(allocator_->Malloc(out.count * sizeof(const Schema*))); - memset(out.schemas, 0, sizeof(Schema*)* out.count); - for (SizeType i = 0; i < out.count; i++) - schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document); - out.begin = validatorCount_; - validatorCount_ += out.count; - } - } - } - -#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX - template - RegexType* CreatePattern(const ValueType& value) { - if (value.IsString()) { - RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString()); - if (!r->IsValid()) { - r->~RegexType(); - AllocatorType::Free(r); - r = 0; - } - return r; - } - return 0; - } - - static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { - GenericRegexSearch rs(*pattern); - return rs.Search(str); - } -#elif RAPIDJSON_SCHEMA_USE_STDREGEX - template - RegexType* CreatePattern(const ValueType& value) { - if (value.IsString()) - try { - return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); - } - catch (const std::regex_error&) { - } - return 0; - } - - static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) { - std::match_results r; - return std::regex_search(str, str + length, r, *pattern); - } -#else - template - RegexType* CreatePattern(const ValueType&) { return 0; } - - static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } -#endif // RAPIDJSON_SCHEMA_USE_STDREGEX - - void AddType(const ValueType& type) { - if (type == GetNullString() ) type_ |= 1 << kNullSchemaType; - else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType; - else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType; - else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType; - else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType; - else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType; - else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); - } - - bool CreateParallelValidator(Context& context) const { - if (enum_ || context.arrayUniqueness) - context.hasher = context.factory.CreateHasher(); - - if (validatorCount_) { - RAPIDJSON_ASSERT(context.validators == 0); - context.validators = static_cast(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); - context.validatorCount = validatorCount_; - - if (allOf_.schemas) - CreateSchemaValidators(context, allOf_); - - if (anyOf_.schemas) - CreateSchemaValidators(context, anyOf_); - - if (oneOf_.schemas) - CreateSchemaValidators(context, oneOf_); - - if (not_) - context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_); - - if (hasSchemaDependencies_) { - for (SizeType i = 0; i < propertyCount_; i++) - if (properties_[i].dependenciesSchema) - context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema); - } - } - - return true; - } - - void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const { - for (SizeType i = 0; i < schemas.count; i++) - context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]); - } - - // O(n) - bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const { - SizeType len = name.GetStringLength(); - const Ch* str = name.GetString(); - for (SizeType index = 0; index < propertyCount_; index++) - if (properties_[index].name.GetStringLength() == len && - (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) - { - *outIndex = index; - return true; - } - return false; - } - - bool CheckInt(Context& context, int64_t i) const { - if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); - - if (!minimum_.IsNull()) { - if (minimum_.IsInt64()) { - if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); - } - else if (minimum_.IsUint64()) { - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64() - } - else if (!CheckDoubleMinimum(context, static_cast(i))) - return false; - } - - if (!maximum_.IsNull()) { - if (maximum_.IsInt64()) { - if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); - } - else if (maximum_.IsUint64()) { } - /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() - else if (!CheckDoubleMaximum(context, static_cast(i))) - return false; - } - - if (!multipleOf_.IsNull()) { - if (multipleOf_.IsUint64()) { - if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); - } - else if (!CheckDoubleMultipleOf(context, static_cast(i))) - return false; - } - - return true; - } - - bool CheckUint(Context& context, uint64_t i) const { - if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); - - if (!minimum_.IsNull()) { - if (minimum_.IsUint64()) { - if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); - } - else if (minimum_.IsInt64()) - /* do nothing */; // i >= 0 > minimum.Getint64() - else if (!CheckDoubleMinimum(context, static_cast(i))) - return false; - } - - if (!maximum_.IsNull()) { - if (maximum_.IsUint64()) { - if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); - } - else if (maximum_.IsInt64()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_ - else if (!CheckDoubleMaximum(context, static_cast(i))) - return false; - } - - if (!multipleOf_.IsNull()) { - if (multipleOf_.IsUint64()) { - if (i % multipleOf_.GetUint64() != 0) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); - } - else if (!CheckDoubleMultipleOf(context, static_cast(i))) - return false; - } - - return true; - } - - bool CheckDoubleMinimum(Context& context, double d) const { - if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); - return true; - } - - bool CheckDoubleMaximum(Context& context, double d) const { - if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); - return true; - } - - bool CheckDoubleMultipleOf(Context& context, double d) const { - double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); - double q = std::floor(a / b); - double r = a - q * b; - if (r > 0.0) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); - return true; - } - - struct Property { - Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} - ~Property() { AllocatorType::Free(dependencies); } - SValue name; - const SchemaType* schema; - const SchemaType* dependenciesSchema; - SizeType dependenciesValidatorIndex; - bool* dependencies; - bool required; - }; - - struct PatternProperty { - PatternProperty() : schema(), pattern() {} - ~PatternProperty() { - if (pattern) { - pattern->~RegexType(); - AllocatorType::Free(pattern); - } - } - const SchemaType* schema; - RegexType* pattern; - }; - - AllocatorType* allocator_; - const SchemaType* typeless_; - uint64_t* enum_; - SizeType enumCount_; - SchemaArray allOf_; - SchemaArray anyOf_; - SchemaArray oneOf_; - const SchemaType* not_; - unsigned type_; // bitmask of kSchemaType - SizeType validatorCount_; - SizeType notValidatorIndex_; - - Property* properties_; - const SchemaType* additionalPropertiesSchema_; - PatternProperty* patternProperties_; - SizeType patternPropertyCount_; - SizeType propertyCount_; - SizeType minProperties_; - SizeType maxProperties_; - bool additionalProperties_; - bool hasDependencies_; - bool hasRequired_; - bool hasSchemaDependencies_; - - const SchemaType* additionalItemsSchema_; - const SchemaType* itemsList_; - const SchemaType** itemsTuple_; - SizeType itemsTupleCount_; - SizeType minItems_; - SizeType maxItems_; - bool additionalItems_; - bool uniqueItems_; - - RegexType* pattern_; - SizeType minLength_; - SizeType maxLength_; - - SValue minimum_; - SValue maximum_; - SValue multipleOf_; - bool exclusiveMinimum_; - bool exclusiveMaximum_; -}; - -template -struct TokenHelper { - RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { - *documentStack.template Push() = '/'; - char buffer[21]; - size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); - for (size_t i = 0; i < length; i++) - *documentStack.template Push() = static_cast(buffer[i]); - } -}; - -// Partial specialized version for char to prevent buffer copying. -template -struct TokenHelper { - RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { - if (sizeof(SizeType) == 4) { - char *buffer = documentStack.template Push(1 + 10); // '/' + uint - *buffer++ = '/'; - const char* end = internal::u32toa(index, buffer); - documentStack.template Pop(static_cast(10 - (end - buffer))); - } - else { - char *buffer = documentStack.template Push(1 + 20); // '/' + uint64 - *buffer++ = '/'; - const char* end = internal::u64toa(index, buffer); - documentStack.template Pop(static_cast(20 - (end - buffer))); - } - } -}; - -} // namespace internal - -/////////////////////////////////////////////////////////////////////////////// -// IGenericRemoteSchemaDocumentProvider - -template -class IGenericRemoteSchemaDocumentProvider { -public: - typedef typename SchemaDocumentType::Ch Ch; - - virtual ~IGenericRemoteSchemaDocumentProvider() {} - virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; -}; - -/////////////////////////////////////////////////////////////////////////////// -// GenericSchemaDocument - -//! JSON schema document. -/*! - A JSON schema document is a compiled version of a JSON schema. - It is basically a tree of internal::Schema. - - \note This is an immutable class (i.e. its instance cannot be modified after construction). - \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding. - \tparam Allocator Allocator type for allocating memory of this document. -*/ -template -class GenericSchemaDocument { -public: - typedef ValueT ValueType; - typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProviderType; - typedef Allocator AllocatorType; - typedef typename ValueType::EncodingType EncodingType; - typedef typename EncodingType::Ch Ch; - typedef internal::Schema SchemaType; - typedef GenericPointer PointerType; - friend class internal::Schema; - template - friend class GenericSchemaValidator; - - //! Constructor. - /*! - Compile a JSON document into schema document. - - \param document A JSON document as source. - \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. - \param allocator An optional allocator instance for allocating memory. Can be null. - */ - explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : - remoteProvider_(remoteProvider), - allocator_(allocator), - ownAllocator_(), - root_(), - typeless_(), - schemaMap_(allocator, kInitialSchemaMapSize), - schemaRef_(allocator, kInitialSchemaRefSize) - { - if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); - - typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); - new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0); - - // Generate root schema, it will call CreateSchema() to create sub-schemas, - // And call AddRefSchema() if there are $ref. - CreateSchemaRecursive(&root_, PointerType(), document, document); - - // Resolve $ref - while (!schemaRef_.Empty()) { - SchemaRefEntry* refEntry = schemaRef_.template Pop(1); - if (const SchemaType* s = GetSchema(refEntry->target)) { - if (refEntry->schema) - *refEntry->schema = s; - - // Create entry in map if not exist - if (!GetSchema(refEntry->source)) { - new (schemaMap_.template Push()) SchemaEntry(refEntry->source, const_cast(s), false, allocator_); - } - } - else if (refEntry->schema) - *refEntry->schema = typeless_; - - refEntry->~SchemaRefEntry(); - } - - RAPIDJSON_ASSERT(root_ != 0); - - schemaRef_.ShrinkToFit(); // Deallocate all memory for ref - } - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - //! Move constructor in C++11 - GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT : - remoteProvider_(rhs.remoteProvider_), - allocator_(rhs.allocator_), - ownAllocator_(rhs.ownAllocator_), - root_(rhs.root_), - typeless_(rhs.typeless_), - schemaMap_(std::move(rhs.schemaMap_)), - schemaRef_(std::move(rhs.schemaRef_)) - { - rhs.remoteProvider_ = 0; - rhs.allocator_ = 0; - rhs.ownAllocator_ = 0; - rhs.typeless_ = 0; - } -#endif - - //! Destructor - ~GenericSchemaDocument() { - while (!schemaMap_.Empty()) - schemaMap_.template Pop(1)->~SchemaEntry(); - - if (typeless_) { - typeless_->~SchemaType(); - Allocator::Free(typeless_); - } - - RAPIDJSON_DELETE(ownAllocator_); - } - - //! Get the root schema. - const SchemaType& GetRoot() const { return *root_; } - -private: - //! Prohibit copying - GenericSchemaDocument(const GenericSchemaDocument&); - //! Prohibit assignment - GenericSchemaDocument& operator=(const GenericSchemaDocument&); - - struct SchemaRefEntry { - SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {} - PointerType source; - PointerType target; - const SchemaType** schema; - }; - - struct SchemaEntry { - SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} - ~SchemaEntry() { - if (owned) { - schema->~SchemaType(); - Allocator::Free(schema); - } - } - PointerType pointer; - SchemaType* schema; - bool owned; - }; - - void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { - if (schema) - *schema = typeless_; - - if (v.GetType() == kObjectType) { - const SchemaType* s = GetSchema(pointer); - if (!s) - CreateSchema(schema, pointer, v, document); - - for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) - CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document); - } - else if (v.GetType() == kArrayType) - for (SizeType i = 0; i < v.Size(); i++) - CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document); - } - - void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { - RAPIDJSON_ASSERT(pointer.IsValid()); - if (v.IsObject()) { - if (!HandleRefSchema(pointer, schema, v, document)) { - SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_); - new (schemaMap_.template Push()) SchemaEntry(pointer, s, true, allocator_); - if (schema) - *schema = s; - } - } - } - - bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) { - static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' }; - static const ValueType kRefValue(kRefString, 4); - - typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue); - if (itr == v.MemberEnd()) - return false; - - if (itr->value.IsString()) { - SizeType len = itr->value.GetStringLength(); - if (len > 0) { - const Ch* s = itr->value.GetString(); - SizeType i = 0; - while (i < len && s[i] != '#') // Find the first # - i++; - - if (i > 0) { // Remote reference, resolve immediately - if (remoteProvider_) { - if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) { - PointerType pointer(&s[i], len - i, allocator_); - if (pointer.IsValid()) { - if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) { - if (schema) - *schema = sc; - return true; - } - } - } - } - } - else if (s[i] == '#') { // Local reference, defer resolution - PointerType pointer(&s[i], len - i, allocator_); - if (pointer.IsValid()) { - if (const ValueType* nv = pointer.Get(document)) - if (HandleRefSchema(source, schema, *nv, document)) - return true; - - new (schemaRef_.template Push()) SchemaRefEntry(source, pointer, schema, allocator_); - return true; - } - } - } - } - return false; - } - - const SchemaType* GetSchema(const PointerType& pointer) const { - for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) - if (pointer == target->pointer) - return target->schema; - return 0; - } - - PointerType GetPointer(const SchemaType* schema) const { - for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) - if (schema == target->schema) - return target->pointer; - return PointerType(); - } - - const SchemaType* GetTypeless() const { return typeless_; } - - static const size_t kInitialSchemaMapSize = 64; - static const size_t kInitialSchemaRefSize = 64; - - IRemoteSchemaDocumentProviderType* remoteProvider_; - Allocator *allocator_; - Allocator *ownAllocator_; - const SchemaType* root_; //!< Root schema. - SchemaType* typeless_; - internal::Stack schemaMap_; // Stores created Pointer -> Schemas - internal::Stack schemaRef_; // Stores Pointer from $ref and schema which holds the $ref -}; - -//! GenericSchemaDocument using Value type. -typedef GenericSchemaDocument SchemaDocument; -//! IGenericRemoteSchemaDocumentProvider using SchemaDocument. -typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; - -/////////////////////////////////////////////////////////////////////////////// -// GenericSchemaValidator - -//! JSON Schema Validator. -/*! - A SAX style JSON schema validator. - It uses a \c GenericSchemaDocument to validate SAX events. - It delegates the incoming SAX events to an output handler. - The default output handler does nothing. - It can be reused multiple times by calling \c Reset(). - - \tparam SchemaDocumentType Type of schema document. - \tparam OutputHandler Type of output handler. Default handler does nothing. - \tparam StateAllocator Allocator for storing the internal validation states. -*/ -template < - typename SchemaDocumentType, - typename OutputHandler = BaseReaderHandler, - typename StateAllocator = CrtAllocator> -class GenericSchemaValidator : - public internal::ISchemaStateFactory, - public internal::ISchemaValidator -{ -public: - typedef typename SchemaDocumentType::SchemaType SchemaType; - typedef typename SchemaDocumentType::PointerType PointerType; - typedef typename SchemaType::EncodingType EncodingType; - typedef typename EncodingType::Ch Ch; - - //! Constructor without output handler. - /*! - \param schemaDocument The schema document to conform to. - \param allocator Optional allocator for storing internal validation states. - \param schemaStackCapacity Optional initial capacity of schema path stack. - \param documentStackCapacity Optional initial capacity of document path stack. - */ - GenericSchemaValidator( - const SchemaDocumentType& schemaDocument, - StateAllocator* allocator = 0, - size_t schemaStackCapacity = kDefaultSchemaStackCapacity, - size_t documentStackCapacity = kDefaultDocumentStackCapacity) - : - schemaDocument_(&schemaDocument), - root_(schemaDocument.GetRoot()), - stateAllocator_(allocator), - ownStateAllocator_(0), - schemaStack_(allocator, schemaStackCapacity), - documentStack_(allocator, documentStackCapacity), - outputHandler_(0), - valid_(true) -#if RAPIDJSON_SCHEMA_VERBOSE - , depth_(0) -#endif - { - } - - //! Constructor with output handler. - /*! - \param schemaDocument The schema document to conform to. - \param allocator Optional allocator for storing internal validation states. - \param schemaStackCapacity Optional initial capacity of schema path stack. - \param documentStackCapacity Optional initial capacity of document path stack. - */ - GenericSchemaValidator( - const SchemaDocumentType& schemaDocument, - OutputHandler& outputHandler, - StateAllocator* allocator = 0, - size_t schemaStackCapacity = kDefaultSchemaStackCapacity, - size_t documentStackCapacity = kDefaultDocumentStackCapacity) - : - schemaDocument_(&schemaDocument), - root_(schemaDocument.GetRoot()), - stateAllocator_(allocator), - ownStateAllocator_(0), - schemaStack_(allocator, schemaStackCapacity), - documentStack_(allocator, documentStackCapacity), - outputHandler_(&outputHandler), - valid_(true) -#if RAPIDJSON_SCHEMA_VERBOSE - , depth_(0) -#endif - { - } - - //! Destructor. - ~GenericSchemaValidator() { - Reset(); - RAPIDJSON_DELETE(ownStateAllocator_); - } - - //! Reset the internal states. - void Reset() { - while (!schemaStack_.Empty()) - PopSchema(); - documentStack_.Clear(); - valid_ = true; - } - - //! Checks whether the current state is valid. - // Implementation of ISchemaValidator - virtual bool IsValid() const { return valid_; } - - //! Gets the JSON pointer pointed to the invalid schema. - PointerType GetInvalidSchemaPointer() const { - return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema()); - } - - //! Gets the keyword of invalid schema. - const Ch* GetInvalidSchemaKeyword() const { - return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword; - } - - //! Gets the JSON pointer pointed to the invalid value. - PointerType GetInvalidDocumentPointer() const { - return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); - } - -#if RAPIDJSON_SCHEMA_VERBOSE -#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \ -RAPIDJSON_MULTILINEMACRO_BEGIN\ - *documentStack_.template Push() = '\0';\ - documentStack_.template Pop(1);\ - internal::PrintInvalidDocument(documentStack_.template Bottom());\ -RAPIDJSON_MULTILINEMACRO_END -#else -#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() -#endif - -#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ - if (!valid_) return false; \ - if (!BeginValue() || !CurrentSchema().method arg1) {\ - RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\ - return valid_ = false;\ - } - -#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ - for (Context* context = schemaStack_.template Bottom(); context != schemaStack_.template End(); context++) {\ - if (context->hasher)\ - static_cast(context->hasher)->method arg2;\ - if (context->validators)\ - for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\ - static_cast(context->validators[i_])->method arg2;\ - if (context->patternPropertiesValidators)\ - for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\ - static_cast(context->patternPropertiesValidators[i_])->method arg2;\ - } - -#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ - return valid_ = EndValue() && (!outputHandler_ || outputHandler_->method arg2) - -#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ - RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ - RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ - RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) - - bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); } - bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } - bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } - bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } - bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } - bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } - bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } - bool RawNumber(const Ch* str, SizeType length, bool copy) - { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } - bool String(const Ch* str, SizeType length, bool copy) - { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } - - bool StartObject() { - RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); - RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); - return valid_ = !outputHandler_ || outputHandler_->StartObject(); - } - - bool Key(const Ch* str, SizeType len, bool copy) { - if (!valid_) return false; - AppendToken(str, len); - if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; - RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); - return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); - } - - bool EndObject(SizeType memberCount) { - if (!valid_) return false; - RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); - if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false; - RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); - } - - bool StartArray() { - RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); - RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); - return valid_ = !outputHandler_ || outputHandler_->StartArray(); - } - - bool EndArray(SizeType elementCount) { - if (!valid_) return false; - RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); - if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false; - RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); - } - -#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_ -#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ -#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ -#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ - - // Implementation of ISchemaStateFactory - virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) { - return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, -#if RAPIDJSON_SCHEMA_VERBOSE - depth_ + 1, -#endif - &GetStateAllocator()); - } - - virtual void DestroySchemaValidator(ISchemaValidator* validator) { - GenericSchemaValidator* v = static_cast(validator); - v->~GenericSchemaValidator(); - StateAllocator::Free(v); - } - - virtual void* CreateHasher() { - return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator()); - } - - virtual uint64_t GetHashCode(void* hasher) { - return static_cast(hasher)->GetHashCode(); - } - - virtual void DestroryHasher(void* hasher) { - HasherType* h = static_cast(hasher); - h->~HasherType(); - StateAllocator::Free(h); - } - - virtual void* MallocState(size_t size) { - return GetStateAllocator().Malloc(size); - } - - virtual void FreeState(void* p) { - StateAllocator::Free(p); - } - -private: - typedef typename SchemaType::Context Context; - typedef GenericValue, StateAllocator> HashCodeArray; - typedef internal::Hasher HasherType; - - GenericSchemaValidator( - const SchemaDocumentType& schemaDocument, - const SchemaType& root, -#if RAPIDJSON_SCHEMA_VERBOSE - unsigned depth, -#endif - StateAllocator* allocator = 0, - size_t schemaStackCapacity = kDefaultSchemaStackCapacity, - size_t documentStackCapacity = kDefaultDocumentStackCapacity) - : - schemaDocument_(&schemaDocument), - root_(root), - stateAllocator_(allocator), - ownStateAllocator_(0), - schemaStack_(allocator, schemaStackCapacity), - documentStack_(allocator, documentStackCapacity), - outputHandler_(0), - valid_(true) -#if RAPIDJSON_SCHEMA_VERBOSE - , depth_(depth) -#endif - { - } - - StateAllocator& GetStateAllocator() { - if (!stateAllocator_) - stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)(); - return *stateAllocator_; - } - - bool BeginValue() { - if (schemaStack_.Empty()) - PushSchema(root_); - else { - if (CurrentContext().inArray) - internal::TokenHelper, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); - - if (!CurrentSchema().BeginValue(CurrentContext())) - return false; - - SizeType count = CurrentContext().patternPropertiesSchemaCount; - const SchemaType** sa = CurrentContext().patternPropertiesSchemas; - typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; - bool valueUniqueness = CurrentContext().valueUniqueness; - RAPIDJSON_ASSERT(CurrentContext().valueSchema); - PushSchema(*CurrentContext().valueSchema); - - if (count > 0) { - CurrentContext().objectPatternValidatorType = patternValidatorType; - ISchemaValidator**& va = CurrentContext().patternPropertiesValidators; - SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount; - va = static_cast(MallocState(sizeof(ISchemaValidator*) * count)); - for (SizeType i = 0; i < count; i++) - va[validatorCount++] = CreateSchemaValidator(*sa[i]); - } - - CurrentContext().arrayUniqueness = valueUniqueness; - } - return true; - } - - bool EndValue() { - if (!CurrentSchema().EndValue(CurrentContext())) - return false; - -#if RAPIDJSON_SCHEMA_VERBOSE - GenericStringBuffer sb; - schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb); - - *documentStack_.template Push() = '\0'; - documentStack_.template Pop(1); - internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom()); -#endif - - uint64_t h = CurrentContext().arrayUniqueness ? static_cast(CurrentContext().hasher)->GetHashCode() : 0; - - PopSchema(); - - if (!schemaStack_.Empty()) { - Context& context = CurrentContext(); - if (context.valueUniqueness) { - HashCodeArray* a = static_cast(context.arrayElementHashCodes); - if (!a) - CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); - for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) - if (itr->GetUint64() == h) - RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString()); - a->PushBack(h, GetStateAllocator()); - } - } - - // Remove the last token of document pointer - while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') - ; - - return true; - } - - void AppendToken(const Ch* str, SizeType len) { - documentStack_.template Reserve(1 + len * 2); // worst case all characters are escaped as two characters - *documentStack_.template PushUnsafe() = '/'; - for (SizeType i = 0; i < len; i++) { - if (str[i] == '~') { - *documentStack_.template PushUnsafe() = '~'; - *documentStack_.template PushUnsafe() = '0'; - } - else if (str[i] == '/') { - *documentStack_.template PushUnsafe() = '~'; - *documentStack_.template PushUnsafe() = '1'; - } - else - *documentStack_.template PushUnsafe() = str[i]; - } - } - - RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, &schema); } - - RAPIDJSON_FORCEINLINE void PopSchema() { - Context* c = schemaStack_.template Pop(1); - if (HashCodeArray* a = static_cast(c->arrayElementHashCodes)) { - a->~HashCodeArray(); - StateAllocator::Free(a); - } - c->~Context(); - } - - const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } - Context& CurrentContext() { return *schemaStack_.template Top(); } - const Context& CurrentContext() const { return *schemaStack_.template Top(); } - - static const size_t kDefaultSchemaStackCapacity = 1024; - static const size_t kDefaultDocumentStackCapacity = 256; - const SchemaDocumentType* schemaDocument_; - const SchemaType& root_; - StateAllocator* stateAllocator_; - StateAllocator* ownStateAllocator_; - internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) - internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) - OutputHandler* outputHandler_; - bool valid_; -#if RAPIDJSON_SCHEMA_VERBOSE - unsigned depth_; -#endif -}; - -typedef GenericSchemaValidator SchemaValidator; - -/////////////////////////////////////////////////////////////////////////////// -// SchemaValidatingReader - -//! A helper class for parsing with validation. -/*! - This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate(). - - \tparam parseFlags Combination of \ref ParseFlag. - \tparam InputStream Type of input stream, implementing Stream concept. - \tparam SourceEncoding Encoding of the input stream. - \tparam SchemaDocumentType Type of schema document. - \tparam StackAllocator Allocator type for stack. -*/ -template < - unsigned parseFlags, - typename InputStream, - typename SourceEncoding, - typename SchemaDocumentType = SchemaDocument, - typename StackAllocator = CrtAllocator> -class SchemaValidatingReader { -public: - typedef typename SchemaDocumentType::PointerType PointerType; - typedef typename InputStream::Ch Ch; - - //! Constructor - /*! - \param is Input stream. - \param sd Schema document. - */ - SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {} - - template - bool operator()(Handler& handler) { - GenericReader reader; - GenericSchemaValidator validator(sd_, handler); - parseResult_ = reader.template Parse(is_, validator); - - isValid_ = validator.IsValid(); - if (isValid_) { - invalidSchemaPointer_ = PointerType(); - invalidSchemaKeyword_ = 0; - invalidDocumentPointer_ = PointerType(); - } - else { - invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); - invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); - invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); - } - - return parseResult_; - } - - const ParseResult& GetParseResult() const { return parseResult_; } - bool IsValid() const { return isValid_; } - const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } - const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } - const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } - -private: - InputStream& is_; - const SchemaDocumentType& sd_; - - ParseResult parseResult_; - PointerType invalidSchemaPointer_; - const Ch* invalidSchemaKeyword_; - PointerType invalidDocumentPointer_; - bool isValid_; -}; - -RAPIDJSON_NAMESPACE_END -RAPIDJSON_DIAG_POP - -#endif // RAPIDJSON_SCHEMA_H_ diff --git a/contrib/rapidjson/include/rapidjson/stream.h b/contrib/rapidjson/include/rapidjson/stream.h deleted file mode 100644 index fef82c252..000000000 --- a/contrib/rapidjson/include/rapidjson/stream.h +++ /dev/null @@ -1,179 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#include "rapidjson.h" - -#ifndef RAPIDJSON_STREAM_H_ -#define RAPIDJSON_STREAM_H_ - -#include "encodings.h" - -RAPIDJSON_NAMESPACE_BEGIN - -/////////////////////////////////////////////////////////////////////////////// -// Stream - -/*! \class rapidjson::Stream - \brief Concept for reading and writing characters. - - For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). - - For write-only stream, only need to implement Put() and Flush(). - -\code -concept Stream { - typename Ch; //!< Character type of the stream. - - //! Read the current character from stream without moving the read cursor. - Ch Peek() const; - - //! Read the current character from stream and moving the read cursor to next character. - Ch Take(); - - //! Get the current read cursor. - //! \return Number of characters read from start. - size_t Tell(); - - //! Begin writing operation at the current read pointer. - //! \return The begin writer pointer. - Ch* PutBegin(); - - //! Write a character. - void Put(Ch c); - - //! Flush the buffer. - void Flush(); - - //! End the writing operation. - //! \param begin The begin write pointer returned by PutBegin(). - //! \return Number of characters written. - size_t PutEnd(Ch* begin); -} -\endcode -*/ - -//! Provides additional information for stream. -/*! - By using traits pattern, this type provides a default configuration for stream. - For custom stream, this type can be specialized for other configuration. - See TEST(Reader, CustomStringStream) in readertest.cpp for example. -*/ -template -struct StreamTraits { - //! Whether to make local copy of stream for optimization during parsing. - /*! - By default, for safety, streams do not use local copy optimization. - Stream that can be copied fast should specialize this, like StreamTraits. - */ - enum { copyOptimization = 0 }; -}; - -//! Reserve n characters for writing to a stream. -template -inline void PutReserve(Stream& stream, size_t count) { - (void)stream; - (void)count; -} - -//! Write character to a stream, presuming buffer is reserved. -template -inline void PutUnsafe(Stream& stream, typename Stream::Ch c) { - stream.Put(c); -} - -//! Put N copies of a character to a stream. -template -inline void PutN(Stream& stream, Ch c, size_t n) { - PutReserve(stream, n); - for (size_t i = 0; i < n; i++) - PutUnsafe(stream, c); -} - -/////////////////////////////////////////////////////////////////////////////// -// StringStream - -//! Read-only string stream. -/*! \note implements Stream concept -*/ -template -struct GenericStringStream { - typedef typename Encoding::Ch Ch; - - GenericStringStream(const Ch *src) : src_(src), head_(src) {} - - Ch Peek() const { return *src_; } - Ch Take() { return *src_++; } - size_t Tell() const { return static_cast(src_ - head_); } - - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - void Put(Ch) { RAPIDJSON_ASSERT(false); } - void Flush() { RAPIDJSON_ASSERT(false); } - size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } - - const Ch* src_; //!< Current read position. - const Ch* head_; //!< Original head of the string. -}; - -template -struct StreamTraits > { - enum { copyOptimization = 1 }; -}; - -//! String stream with UTF8 encoding. -typedef GenericStringStream > StringStream; - -/////////////////////////////////////////////////////////////////////////////// -// InsituStringStream - -//! A read-write string stream. -/*! This string stream is particularly designed for in-situ parsing. - \note implements Stream concept -*/ -template -struct GenericInsituStringStream { - typedef typename Encoding::Ch Ch; - - GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} - - // Read - Ch Peek() { return *src_; } - Ch Take() { return *src_++; } - size_t Tell() { return static_cast(src_ - head_); } - - // Write - void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } - - Ch* PutBegin() { return dst_ = src_; } - size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } - void Flush() {} - - Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } - void Pop(size_t count) { dst_ -= count; } - - Ch* src_; - Ch* dst_; - Ch* head_; -}; - -template -struct StreamTraits > { - enum { copyOptimization = 1 }; -}; - -//! Insitu string stream with UTF8 encoding. -typedef GenericInsituStringStream > InsituStringStream; - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_STREAM_H_ diff --git a/contrib/rapidjson/include/rapidjson/stringbuffer.h b/contrib/rapidjson/include/rapidjson/stringbuffer.h index 4e38b82c3..1c9c80b79 100644 --- a/contrib/rapidjson/include/rapidjson/stringbuffer.h +++ b/contrib/rapidjson/include/rapidjson/stringbuffer.h @@ -15,8 +15,7 @@ #ifndef RAPIDJSON_STRINGBUFFER_H_ #define RAPIDJSON_STRINGBUFFER_H_ -#include "stream.h" -#include "internal/stack.h" +#include "rapidjson.h" #if RAPIDJSON_HAS_CXX11_RVALUE_REFS #include // std::move @@ -24,11 +23,6 @@ #include "internal/stack.h" -#if defined(__clang__) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(c++98-compat) -#endif - RAPIDJSON_NAMESPACE_BEGIN //! Represents an in-memory output stream. @@ -54,7 +48,6 @@ public: #endif void Put(Ch c) { *stack_.template Push() = c; } - void PutUnsafe(Ch c) { *stack_.template PushUnsafe() = c; } void Flush() {} void Clear() { stack_.Clear(); } @@ -64,10 +57,7 @@ public: stack_.ShrinkToFit(); stack_.template Pop(1); } - - void Reserve(size_t count) { stack_.template Reserve(count); } Ch* Push(size_t count) { return stack_.template Push(count); } - Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe(count); } void Pop(size_t count) { stack_.template Pop(count); } const Ch* GetString() const { @@ -78,12 +68,8 @@ public: return stack_.template Bottom(); } - //! Get the size of string in bytes in the string buffer. size_t GetSize() const { return stack_.GetSize(); } - //! Get the length of string in Ch in the string buffer. - size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); } - static const size_t kDefaultCapacity = 256; mutable internal::Stack stack_; @@ -96,16 +82,6 @@ private: //! String buffer with UTF8 encoding typedef GenericStringBuffer > StringBuffer; -template -inline void PutReserve(GenericStringBuffer& stream, size_t count) { - stream.Reserve(count); -} - -template -inline void PutUnsafe(GenericStringBuffer& stream, typename Encoding::Ch c) { - stream.PutUnsafe(c); -} - //! Implement specialized version of PutN() with memset() for better performance. template<> inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { @@ -114,8 +90,4 @@ inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { RAPIDJSON_NAMESPACE_END -#if defined(__clang__) -RAPIDJSON_DIAG_POP -#endif - #endif // RAPIDJSON_STRINGBUFFER_H_ diff --git a/contrib/rapidjson/include/rapidjson/writer.h b/contrib/rapidjson/include/rapidjson/writer.h index e610ebb60..e1eea38b9 100644 --- a/contrib/rapidjson/include/rapidjson/writer.h +++ b/contrib/rapidjson/include/rapidjson/writer.h @@ -15,8 +15,7 @@ #ifndef RAPIDJSON_WRITER_H_ #define RAPIDJSON_WRITER_H_ -#include "stream.h" -#include "internal/meta.h" +#include "rapidjson.h" #include "internal/stack.h" #include "internal/strfunc.h" #include "internal/dtoa.h" @@ -24,16 +23,8 @@ #include "stringbuffer.h" #include // placement new -#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) -#include -#pragma intrinsic(_BitScanForward) -#endif -#ifdef RAPIDJSON_SSE42 -#include -#elif defined(RAPIDJSON_SSE2) -#include -#elif defined(RAPIDJSON_NEON) -#include +#if RAPIDJSON_HAS_STDSTRING +#include #endif #ifdef _MSC_VER @@ -41,36 +32,8 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant #endif -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(padded) -RAPIDJSON_DIAG_OFF(unreachable-code) -RAPIDJSON_DIAG_OFF(c++98-compat) -#endif - RAPIDJSON_NAMESPACE_BEGIN -/////////////////////////////////////////////////////////////////////////////// -// WriteFlag - -/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS - \ingroup RAPIDJSON_CONFIG - \brief User-defined kWriteDefaultFlags definition. - - User can define this as any \c WriteFlag combinations. -*/ -#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS -#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags -#endif - -//! Combination of writeFlags -enum WriteFlag { - kWriteNoFlags = 0, //!< No flags are set. - kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings. - kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN. - kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS -}; - //! JSON writer /*! Writer implements the concept Handler. It generates JSON text by events to an output os. @@ -87,13 +50,11 @@ enum WriteFlag { \tparam StackAllocator Type of allocator for allocating memory of stack. \note implements Handler concept */ -template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator> class Writer { public: typedef typename SourceEncoding::Ch Ch; - static const int kDefaultMaxDecimalPlaces = 324; - //! Constructor /*! \param os Output stream. \param stackAllocator User supplied allocator. If it is null, it will create a private one. @@ -101,18 +62,11 @@ public: */ explicit Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : - os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), hasRoot_(false) {} explicit Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : - os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - Writer(Writer&& rhs) : - os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) { - rhs.os_ = 0; - } -#endif + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), hasRoot_(false) {} //! Reset the writer with a new stream. /*! @@ -146,66 +100,29 @@ public: return hasRoot_ && level_stack_.Empty(); } - int GetMaxDecimalPlaces() const { - return maxDecimalPlaces_; - } - - //! Sets the maximum number of decimal places for double output. - /*! - This setting truncates the output with specified number of decimal places. - - For example, - - \code - writer.SetMaxDecimalPlaces(3); - writer.StartArray(); - writer.Double(0.12345); // "0.123" - writer.Double(0.0001); // "0.0" - writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent) - writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent) - writer.EndArray(); - \endcode - - The default setting does not truncate any decimal places. You can restore to this setting by calling - \code - writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces); - \endcode - */ - void SetMaxDecimalPlaces(int maxDecimalPlaces) { - maxDecimalPlaces_ = maxDecimalPlaces; - } - /*!@name Implementation of Handler \see Handler */ //@{ - bool Null() { Prefix(kNullType); return EndValue(WriteNull()); } - bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); } - bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); } - bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); } - bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); } - bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(WriteUint64(u64)); } + bool Null() { Prefix(kNullType); return WriteNull(); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return WriteBool(b); } + bool Int(int i) { Prefix(kNumberType); return WriteInt(i); } + bool Uint(unsigned u) { Prefix(kNumberType); return WriteUint(u); } + bool Int64(int64_t i64) { Prefix(kNumberType); return WriteInt64(i64); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return WriteUint64(u64); } //! Writes the given \c double value to the stream /*! \param d The value to be written. \return Whether it is succeed. */ - bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); } - - bool RawNumber(const Ch* str, SizeType length, bool copy = false) { - RAPIDJSON_ASSERT(str != 0); - (void)copy; - Prefix(kNumberType); - return EndValue(WriteString(str, length)); - } + bool Double(double d) { Prefix(kNumberType); return WriteDouble(d); } bool String(const Ch* str, SizeType length, bool copy = false) { - RAPIDJSON_ASSERT(str != 0); (void)copy; Prefix(kStringType); - return EndValue(WriteString(str, length)); + return WriteString(str, length); } #if RAPIDJSON_HAS_STDSTRING @@ -221,21 +138,16 @@ public: } bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } - -#if RAPIDJSON_HAS_STDSTRING - bool Key(const std::basic_string& str) - { - return Key(str.data(), SizeType(str.size())); - } -#endif bool EndObject(SizeType memberCount = 0) { (void)memberCount; - RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object - RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); // currently inside an Array, not Object - RAPIDJSON_ASSERT(0 == level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); level_stack_.template Pop(1); - return EndValue(WriteEndObject()); + bool ret = WriteEndObject(); + if (level_stack_.Empty()) // end of json text + os_->Flush(); + return ret; } bool StartArray() { @@ -249,7 +161,10 @@ public: RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); level_stack_.template Pop(1); - return EndValue(WriteEndArray()); + bool ret = WriteEndArray(); + if (level_stack_.Empty()) // end of json text + os_->Flush(); + return ret; } //@} @@ -257,33 +172,11 @@ public: //@{ //! Simpler but slower overload. - bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); } - bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); } - + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + //@} - //! Write a raw JSON value. - /*! - For user to write a stringified JSON as a value. - - \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. - \param length Length of the json. - \param type Type of the root of json. - */ - bool RawValue(const Ch* json, size_t length, Type type) { - RAPIDJSON_ASSERT(json != 0); - Prefix(type); - return EndValue(WriteRawValue(json, length)); - } - - //! Flush the output stream. - /*! - Allows the user to flush the output stream immediately. - */ - void Flush() { - os_->Flush(); - } - protected: //! Information for each nested level struct Level { @@ -295,18 +188,15 @@ protected: static const size_t kDefaultLevelDepth = 32; bool WriteNull() { - PutReserve(*os_, 4); - PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true; + os_->Put('n'); os_->Put('u'); os_->Put('l'); os_->Put('l'); return true; } bool WriteBool(bool b) { if (b) { - PutReserve(*os_, 4); - PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e'); + os_->Put('t'); os_->Put('r'); os_->Put('u'); os_->Put('e'); } else { - PutReserve(*os_, 5); - PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e'); + os_->Put('f'); os_->Put('a'); os_->Put('l'); os_->Put('s'); os_->Put('e'); } return true; } @@ -314,69 +204,45 @@ protected: bool WriteInt(int i) { char buffer[11]; const char* end = internal::i32toa(i, buffer); - PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + os_->Put(*p); return true; } bool WriteUint(unsigned u) { char buffer[10]; const char* end = internal::u32toa(u, buffer); - PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + os_->Put(*p); return true; } bool WriteInt64(int64_t i64) { char buffer[21]; const char* end = internal::i64toa(i64, buffer); - PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + os_->Put(*p); return true; } bool WriteUint64(uint64_t u64) { char buffer[20]; char* end = internal::u64toa(u64, buffer); - PutReserve(*os_, static_cast(end - buffer)); for (char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + os_->Put(*p); return true; } bool WriteDouble(double d) { - if (internal::Double(d).IsNanOrInf()) { - if (!(writeFlags & kWriteNanAndInfFlag)) - return false; - if (internal::Double(d).IsNan()) { - PutReserve(*os_, 3); - PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); - return true; - } - if (internal::Double(d).Sign()) { - PutReserve(*os_, 9); - PutUnsafe(*os_, '-'); - } - else - PutReserve(*os_, 8); - PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); - PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); - return true; - } - char buffer[25]; - char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); - PutReserve(*os_, static_cast(end - buffer)); + char* end = internal::dtoa(d, buffer); for (char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + os_->Put(*p); return true; } bool WriteString(const Ch* str, SizeType length) { - static const typename OutputStream::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; static const char escape[256] = { #define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //0 1 2 3 4 5 6 7 8 9 A B C D E F @@ -389,27 +255,22 @@ protected: #undef Z16 }; - if (TargetEncoding::supportUnicode) - PutReserve(*os_, 2 + length * 6); // "\uxxxx..." - else - PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..." - - PutUnsafe(*os_, '\"'); + os_->Put('\"'); GenericStringStream is(str); - while (ScanWriteUnescapedString(is, length)) { + while (is.Tell() < length) { const Ch c = is.Peek(); - if (!TargetEncoding::supportUnicode && static_cast(c) >= 0x80) { + if (!TargetEncoding::supportUnicode && (unsigned)c >= 0x80) { // Unicode escaping unsigned codepoint; - if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint))) + if (!SourceEncoding::Decode(is, &codepoint)) return false; - PutUnsafe(*os_, '\\'); - PutUnsafe(*os_, 'u'); + os_->Put('\\'); + os_->Put('u'); if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { - PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]); - PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]); - PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]); - PutUnsafe(*os_, hexDigits[(codepoint ) & 15]); + os_->Put(hexDigits[(codepoint >> 12) & 15]); + os_->Put(hexDigits[(codepoint >> 8) & 15]); + os_->Put(hexDigits[(codepoint >> 4) & 15]); + os_->Put(hexDigits[(codepoint ) & 15]); } else { RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); @@ -417,59 +278,45 @@ protected: unsigned s = codepoint - 0x010000; unsigned lead = (s >> 10) + 0xD800; unsigned trail = (s & 0x3FF) + 0xDC00; - PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]); - PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]); - PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]); - PutUnsafe(*os_, hexDigits[(lead ) & 15]); - PutUnsafe(*os_, '\\'); - PutUnsafe(*os_, 'u'); - PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]); - PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]); - PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]); - PutUnsafe(*os_, hexDigits[(trail ) & 15]); + os_->Put(hexDigits[(lead >> 12) & 15]); + os_->Put(hexDigits[(lead >> 8) & 15]); + os_->Put(hexDigits[(lead >> 4) & 15]); + os_->Put(hexDigits[(lead ) & 15]); + os_->Put('\\'); + os_->Put('u'); + os_->Put(hexDigits[(trail >> 12) & 15]); + os_->Put(hexDigits[(trail >> 8) & 15]); + os_->Put(hexDigits[(trail >> 4) & 15]); + os_->Put(hexDigits[(trail ) & 15]); } } - else if ((sizeof(Ch) == 1 || static_cast(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { + else if ((sizeof(Ch) == 1 || (unsigned)c < 256) && escape[(unsigned char)c]) { is.Take(); - PutUnsafe(*os_, '\\'); - PutUnsafe(*os_, static_cast(escape[static_cast(c)])); - if (escape[static_cast(c)] == 'u') { - PutUnsafe(*os_, '0'); - PutUnsafe(*os_, '0'); - PutUnsafe(*os_, hexDigits[static_cast(c) >> 4]); - PutUnsafe(*os_, hexDigits[static_cast(c) & 0xF]); + os_->Put('\\'); + os_->Put(escape[(unsigned char)c]); + if (escape[(unsigned char)c] == 'u') { + os_->Put('0'); + os_->Put('0'); + os_->Put(hexDigits[(unsigned char)c >> 4]); + os_->Put(hexDigits[(unsigned char)c & 0xF]); } } - else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? - Transcoder::Validate(is, *os_) : - Transcoder::TranscodeUnsafe(is, *os_)))) - return false; + else + if (!Transcoder::Transcode(is, *os_)) + return false; } - PutUnsafe(*os_, '\"'); + os_->Put('\"'); return true; } - bool ScanWriteUnescapedString(GenericStringStream& is, size_t length) { - return RAPIDJSON_LIKELY(is.Tell() < length); - } - bool WriteStartObject() { os_->Put('{'); return true; } bool WriteEndObject() { os_->Put('}'); return true; } bool WriteStartArray() { os_->Put('['); return true; } bool WriteEndArray() { os_->Put(']'); return true; } - bool WriteRawValue(const Ch* json, size_t length) { - PutReserve(*os_, length); - for (size_t i = 0; i < length; i++) { - RAPIDJSON_ASSERT(json[i] != '\0'); - PutUnsafe(*os_, json[i]); - } - return true; - } - void Prefix(Type type) { (void)type; - if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root + if (level_stack_.GetSize() != 0) { // this value is not at root Level* level = level_stack_.template Top(); if (level->valueCount > 0) { if (level->inArray) @@ -487,16 +334,8 @@ protected: } } - // Flush the value if it is the top level one. - bool EndValue(bool ret) { - if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text - Flush(); - return ret; - } - OutputStream* os_; internal::Stack level_stack_; - int maxDecimalPlaces_; bool hasRoot_; private: @@ -511,7 +350,7 @@ template<> inline bool Writer::WriteInt(int i) { char *buffer = os_->Push(11); const char* end = internal::i32toa(i, buffer); - os_->Pop(static_cast(11 - (end - buffer))); + os_->Pop(11 - (end - buffer)); return true; } @@ -519,7 +358,7 @@ template<> inline bool Writer::WriteUint(unsigned u) { char *buffer = os_->Push(10); const char* end = internal::u32toa(u, buffer); - os_->Pop(static_cast(10 - (end - buffer))); + os_->Pop(10 - (end - buffer)); return true; } @@ -527,7 +366,7 @@ template<> inline bool Writer::WriteInt64(int64_t i64) { char *buffer = os_->Push(21); const char* end = internal::i64toa(i64, buffer); - os_->Pop(static_cast(21 - (end - buffer))); + os_->Pop(21 - (end - buffer)); return true; } @@ -535,177 +374,22 @@ template<> inline bool Writer::WriteUint64(uint64_t u) { char *buffer = os_->Push(20); const char* end = internal::u64toa(u, buffer); - os_->Pop(static_cast(20 - (end - buffer))); + os_->Pop(20 - (end - buffer)); return true; } template<> inline bool Writer::WriteDouble(double d) { - if (internal::Double(d).IsNanOrInf()) { - // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag). - if (!(kWriteDefaultFlags & kWriteNanAndInfFlag)) - return false; - if (internal::Double(d).IsNan()) { - PutReserve(*os_, 3); - PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); - return true; - } - if (internal::Double(d).Sign()) { - PutReserve(*os_, 9); - PutUnsafe(*os_, '-'); - } - else - PutReserve(*os_, 8); - PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); - PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); - return true; - } - char *buffer = os_->Push(25); - char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); - os_->Pop(static_cast(25 - (end - buffer))); + char* end = internal::dtoa(d, buffer); + os_->Pop(25 - (end - buffer)); return true; } -#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) -template<> -inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { - if (length < 16) - return RAPIDJSON_LIKELY(is.Tell() < length); - - if (!RAPIDJSON_LIKELY(is.Tell() < length)) - return false; - - const char* p = is.src_; - const char* end = is.head_ + length; - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); - if (nextAligned > end) - return true; - - while (p != nextAligned) - if (*p < 0x20 || *p == '\"' || *p == '\\') { - is.src_ = p; - return RAPIDJSON_LIKELY(is.Tell() < length); - } - else - os_->PutUnsafe(*p++); - - // The rest of string using SIMD - static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; - static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; - const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); - const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); - const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); - - for (; p != endAligned; p += 16) { - const __m128i s = _mm_load_si128(reinterpret_cast(p)); - const __m128i t1 = _mm_cmpeq_epi8(s, dq); - const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F - const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); - unsigned short r = static_cast(_mm_movemask_epi8(x)); - if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped - SizeType len; -#ifdef _MSC_VER // Find the index of first escaped - unsigned long offset; - _BitScanForward(&offset, r); - len = offset; -#else - len = static_cast(__builtin_ffs(r) - 1); -#endif - char* q = reinterpret_cast(os_->PushUnsafe(len)); - for (size_t i = 0; i < len; i++) - q[i] = p[i]; - - p += len; - break; - } - _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s); - } - - is.src_ = p; - return RAPIDJSON_LIKELY(is.Tell() < length); -} -#elif defined(RAPIDJSON_NEON) -template<> -inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { - if (length < 16) - return RAPIDJSON_LIKELY(is.Tell() < length); - - if (!RAPIDJSON_LIKELY(is.Tell() < length)) - return false; - - const char* p = is.src_; - const char* end = is.head_ + length; - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); - if (nextAligned > end) - return true; - - while (p != nextAligned) - if (*p < 0x20 || *p == '\"' || *p == '\\') { - is.src_ = p; - return RAPIDJSON_LIKELY(is.Tell() < length); - } - else - os_->PutUnsafe(*p++); - - // The rest of string using SIMD - const uint8x16_t s0 = vmovq_n_u8('"'); - const uint8x16_t s1 = vmovq_n_u8('\\'); - const uint8x16_t s2 = vmovq_n_u8('\b'); - const uint8x16_t s3 = vmovq_n_u8(32); - - for (; p != endAligned; p += 16) { - const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); - uint8x16_t x = vceqq_u8(s, s0); - x = vorrq_u8(x, vceqq_u8(s, s1)); - x = vorrq_u8(x, vceqq_u8(s, s2)); - x = vorrq_u8(x, vcltq_u8(s, s3)); - - x = vrev64q_u8(x); // Rev in 64 - uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract - uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract - - SizeType len = 0; - bool escaped = false; - if (low == 0) { - if (high != 0) { - unsigned lz = (unsigned)__builtin_clzll(high); - len = 8 + (lz >> 3); - escaped = true; - } - } else { - unsigned lz = (unsigned)__builtin_clzll(low); - len = lz >> 3; - escaped = true; - } - if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped - char* q = reinterpret_cast(os_->PushUnsafe(len)); - for (size_t i = 0; i < len; i++) - q[i] = p[i]; - - p += len; - break; - } - vst1q_u8(reinterpret_cast(os_->PushUnsafe(16)), s); - } - - is.src_ = p; - return RAPIDJSON_LIKELY(is.Tell() < length); -} -#endif // RAPIDJSON_NEON - RAPIDJSON_NAMESPACE_END #ifdef _MSC_VER RAPIDJSON_DIAG_POP #endif -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - #endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/contrib/rapidjson/license.txt b/contrib/rapidjson/license.txt index 7ccc161c8..879293afa 100644 --- a/contrib/rapidjson/license.txt +++ b/contrib/rapidjson/license.txt @@ -3,7 +3,7 @@ Tencent is pleased to support the open source community by making RapidJSON avai Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. If you have downloaded a copy of the RapidJSON binary from Tencent, please note that the RapidJSON binary is licensed under the MIT License. -If you have downloaded a copy of the RapidJSON source code from Tencent, please note that RapidJSON source code is licensed under the MIT License, except for the third-party components listed below which are subject to different license terms. Your integration of RapidJSON into your own projects may require compliance with the MIT License, as well as the other licenses applicable to the third-party components included within RapidJSON. To avoid the problematic JSON license in your own projects, it's sufficient to exclude the bin/jsonchecker/ directory, as it's the only code under the JSON license. +If you have downloaded a copy of the RapidJSON source code from Tencent, please note that RapidJSON source code is licensed under the MIT License, except for the third-party components listed below which are subject to different license terms. Your integration of RapidJSON into your own projects may require compliance with the MIT License, as well as the other licenses applicable to the third-party components included within RapidJSON. A copy of the MIT License is included in this file. Other dependencies and licenses: diff --git a/contrib/rapidjson/readme.md b/contrib/rapidjson/readme.md index b833a98e8..19da38667 100644 --- a/contrib/rapidjson/readme.md +++ b/contrib/rapidjson/readme.md @@ -1,17 +1,17 @@ -![RapidJSON logo](doc/logo/rapidjson.png) +![](doc/logo/rapidjson.png) -![Release version](https://img.shields.io/badge/release-v1.1.0-blue.svg) +![](https://img.shields.io/badge/release-v1.0.2-blue.png) -## A fast JSON parser/generator for C++ with both SAX/DOM style API +## A fast JSON parser/generator for C++ with both SAX/DOM style API Tencent is pleased to support the open source community by making RapidJSON available. Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. -* [RapidJSON GitHub](https://github.com/Tencent/rapidjson/) +* [RapidJSON GitHub](https://github.com/miloyip/rapidjson/) * RapidJSON Documentation - * [English](http://rapidjson.org/) - * [简体中文](http://rapidjson.org/zh-cn/) + * [English](http://miloyip.github.io/rapidjson/) + * [简体中文](http://miloyip.github.io/rapidjson/zh-cn/) * [GitBook](https://www.gitbook.com/book/miloyip/rapidjson/) with downloadable PDF/EPUB/MOBI, without API reference. ## Build status @@ -20,43 +20,33 @@ Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights | :---------------: | :-----------------: | :-------------------: | | ![lin-badge] | ![win-badge] | ![cov-badge] | -[lin-badge]: https://travis-ci.org/Tencent/rapidjson.svg?branch=master "Travis build status" -[lin-link]: https://travis-ci.org/Tencent/rapidjson "Travis build status" -[win-badge]: https://ci.appveyor.com/api/projects/status/l6qulgqahcayidrf/branch/master?svg=true "AppVeyor build status" -[win-link]: https://ci.appveyor.com/project/miloyip/rapidjson-0fdqj/branch/master "AppVeyor build status" -[cov-badge]: https://coveralls.io/repos/Tencent/rapidjson/badge.svg?branch=master "Coveralls coverage" -[cov-link]: https://coveralls.io/r/Tencent/rapidjson?branch=master "Coveralls coverage" +[lin-badge]: https://travis-ci.org/miloyip/rapidjson.png?branch=master "Travis build status" +[lin-link]: https://travis-ci.org/miloyip/rapidjson "Travis build status" +[win-badge]: https://ci.appveyor.com/api/projects/status/u658dcuwxo14a8m9/branch/master "AppVeyor build status" +[win-link]: https://ci.appveyor.com/project/miloyip/rapidjson/branch/master "AppVeyor build status" +[cov-badge]: https://coveralls.io/repos/miloyip/rapidjson/badge.png?branch=master +[cov-link]: https://coveralls.io/r/miloyip/rapidjson?branch=master ## Introduction RapidJSON is a JSON parser and generator for C++. It was inspired by [RapidXml](http://rapidxml.sourceforge.net/). -* RapidJSON is **small** but **complete**. It supports both SAX and DOM style API. The SAX parser is only a half thousand lines of code. +* RapidJSON is small but complete. It supports both SAX and DOM style API. The SAX parser is only a half thousand lines of code. -* RapidJSON is **fast**. Its performance can be comparable to `strlen()`. It also optionally supports SSE2/SSE4.2 for acceleration. +* RapidJSON is fast. Its performance can be comparable to `strlen()`. It also optionally supports SSE2/SSE4.2 for acceleration. -* RapidJSON is **self-contained** and **header-only**. It does not depend on external libraries such as BOOST. It even does not depend on STL. +* RapidJSON is self-contained. It does not depend on external libraries such as BOOST. It even does not depend on STL. -* RapidJSON is **memory-friendly**. Each JSON value occupies exactly 16 bytes for most 32/64-bit machines (excluding text string). By default it uses a fast memory allocator, and the parser allocates memory compactly during parsing. +* RapidJSON is memory friendly. Each JSON value occupies exactly 16/20 bytes for most 32/64-bit machines (excluding text string). By default it uses a fast memory allocator, and the parser allocates memory compactly during parsing. -* RapidJSON is **Unicode-friendly**. It supports UTF-8, UTF-16, UTF-32 (LE & BE), and their detection, validation and transcoding internally. For example, you can read a UTF-8 file and let RapidJSON transcode the JSON strings into UTF-16 in the DOM. It also supports surrogates and "\u0000" (null character). +* RapidJSON is Unicode friendly. It supports UTF-8, UTF-16, UTF-32 (LE & BE), and their detection, validation and transcoding internally. For example, you can read a UTF-8 file and let RapidJSON transcode the JSON strings into UTF-16 in the DOM. It also supports surrogates and "\u0000" (null character). More features can be read [here](doc/features.md). -JSON(JavaScript Object Notation) is a light-weight data exchange format. RapidJSON should be in fully compliance with RFC7159/ECMA-404, with optional support of relaxed syntax. More information about JSON can be obtained at +JSON(JavaScript Object Notation) is a light-weight data exchange format. RapidJSON should be in fully compliance with RFC7159/ECMA-404. More information about JSON can be obtained at * [Introducing JSON](http://json.org/) -* [RFC7159: The JavaScript Object Notation (JSON) Data Interchange Format](https://tools.ietf.org/html/rfc7159) -* [Standard ECMA-404: The JSON Data Interchange Format](https://www.ecma-international.org/publications/standards/Ecma-404.htm) - -## Highlights in v1.1 (2016-8-25) - -* Added [JSON Pointer](doc/pointer.md) -* Added [JSON Schema](doc/schema.md) -* Added [relaxed JSON syntax](doc/dom.md) (comment, trailing comma, NaN/Infinity) -* Iterating array/object with [C++11 Range-based for loop](doc/tutorial.md) -* Reduce memory overhead of each `Value` from 24 bytes to 16 bytes in x86-64 architecture. - -For other changes please refer to [change log](CHANGELOG.md). +* [RFC7159: The JavaScript Object Notation (JSON) Data Interchange Format](http://www.ietf.org/rfc/rfc7159.txt) +* [Standard ECMA-404: The JSON Data Interchange Format](http://www.ecma-international.org/publications/standards/Ecma-404.htm) ## Compatibility @@ -73,9 +63,9 @@ Users can build and run the unit tests on their platform/compiler. RapidJSON is a header-only C++ library. Just copy the `include/rapidjson` folder to system or project's include path. RapidJSON uses following software as its dependencies: -* [CMake](https://cmake.org/) as a general build tool -* (optional) [Doxygen](http://www.doxygen.org) to build documentation -* (optional) [googletest](https://github.com/google/googletest) for unit and performance testing +* [CMake](http://www.cmake.org) as a general build tool +* (optional)[Doxygen](http://www.doxygen.org) to build documentation +* (optional)[googletest](https://code.google.com/p/googletest/) for unit and performance testing To generate user documentation and run tests please proceed with the steps below: @@ -84,7 +74,7 @@ To generate user documentation and run tests please proceed with the steps below 3. Change to `build` directory and run `cmake ..` command to configure your build. Windows users can do the same with cmake-gui application. 4. On Windows, build the solution found in the build directory. On Linux, run `make` from the build directory. -On successful build you will find compiled test and example binaries in `bin` +On successfull build you will find compiled test and example binaries in `bin` directory. The generated documentation will be available in `doc/html` directory of the build tree. To run tests after finished build please run `make test` or `ctest` from your build tree. You can get detailed output using `ctest @@ -136,25 +126,4 @@ The following diagram shows the process. ![simpledom](doc/diagram/simpledom.png) -More [examples](https://github.com/Tencent/rapidjson/tree/master/example) are available: - -* DOM API - * [tutorial](https://github.com/Tencent/rapidjson/blob/master/example/tutorial/tutorial.cpp): Basic usage of DOM API. - -* SAX API - * [simplereader](https://github.com/Tencent/rapidjson/blob/master/example/simplereader/simplereader.cpp): Dumps all SAX events while parsing a JSON by `Reader`. - * [condense](https://github.com/Tencent/rapidjson/blob/master/example/condense/condense.cpp): A command line tool to rewrite a JSON, with all whitespaces removed. - * [pretty](https://github.com/Tencent/rapidjson/blob/master/example/pretty/pretty.cpp): A command line tool to rewrite a JSON with indents and newlines by `PrettyWriter`. - * [capitalize](https://github.com/Tencent/rapidjson/blob/master/example/capitalize/capitalize.cpp): A command line tool to capitalize strings in JSON. - * [messagereader](https://github.com/Tencent/rapidjson/blob/master/example/messagereader/messagereader.cpp): Parse a JSON message with SAX API. - * [serialize](https://github.com/Tencent/rapidjson/blob/master/example/serialize/serialize.cpp): Serialize a C++ object into JSON with SAX API. - * [jsonx](https://github.com/Tencent/rapidjson/blob/master/example/jsonx/jsonx.cpp): Implements a `JsonxWriter` which stringify SAX events into [JSONx](https://www-01.ibm.com/support/knowledgecenter/SS9H2Y_7.1.0/com.ibm.dp.doc/json_jsonx.html) (a kind of XML) format. The example is a command line tool which converts input JSON into JSONx format. - -* Schema - * [schemavalidator](https://github.com/Tencent/rapidjson/blob/master/example/schemavalidator/schemavalidator.cpp) : A command line tool to validate a JSON with a JSON schema. - -* Advanced - * [prettyauto](https://github.com/Tencent/rapidjson/blob/master/example/prettyauto/prettyauto.cpp): A modified version of [pretty](https://github.com/Tencent/rapidjson/blob/master/example/pretty/pretty.cpp) to automatically handle JSON with any UTF encodings. - * [parsebyparts](https://github.com/Tencent/rapidjson/blob/master/example/parsebyparts/parsebyparts.cpp): Implements an `AsyncDocumentParser` which can parse JSON in parts, using C++11 thread. - * [filterkey](https://github.com/Tencent/rapidjson/blob/master/example/filterkey/filterkey.cpp): A command line tool to remove all values with user-specified key. - * [filterkeydom](https://github.com/Tencent/rapidjson/blob/master/example/filterkeydom/filterkeydom.cpp): Same tool as above, but it demonstrates how to use a generator to populate a `Document`. +More [examples](https://github.com/miloyip/rapidjson/tree/master/example) are available. diff --git a/test/unit/TestModelFactory.h b/test/unit/TestModelFactory.h index ca070890d..f848f5536 100644 --- a/test/unit/TestModelFactory.h +++ b/test/unit/TestModelFactory.h @@ -89,7 +89,7 @@ public: scene->mRootNode = new aiNode; scene->mRootNode->mNumMeshes = 1; - scene->mRootNode->mMeshes = new unsigned int[1]{ 0 }; + scene->mRootNode->mMeshes = new unsigned int[scene->mRootNode->mNumMeshes]{ 0 }; return scene; } diff --git a/test/unit/utMetadata.cpp b/test/unit/utMetadata.cpp index 4109b068c..b44de1eb2 100644 --- a/test/unit/utMetadata.cpp +++ b/test/unit/utMetadata.cpp @@ -79,7 +79,6 @@ TEST_F( utMetadata, allocTest ) { EXPECT_EQ( 1U, data->mNumProperties ); EXPECT_NE( nullptr, data->mKeys ); EXPECT_NE( nullptr, data->mValues ); - aiMetadata::Dealloc( data ); } TEST_F( utMetadata, get_set_pod_Test ) { diff --git a/test/unit/utRemoveVCProcess.cpp b/test/unit/utRemoveVCProcess.cpp index 6caa72c11..c78e80d3f 100644 --- a/test/unit/utRemoveVCProcess.cpp +++ b/test/unit/utRemoveVCProcess.cpp @@ -70,7 +70,7 @@ TEST_F( utRevmoveVCProcess, issue1266_ProcessMeshTest_NoCrash ) { mesh->mNumVertices = 1; mesh->mColors[ 0 ] = new aiColor4D[ 2 ]; scene->mMeshes[ 0 ] = mesh; - std::unique_ptr process(new RemoveVCProcess); + RemoveVCProcess *process = new RemoveVCProcess; process->Execute( scene ); delete scene; } From a3053eb35896e8c7122028703d88aa7d1e78ad29 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 4 Oct 2017 20:50:16 +0300 Subject: [PATCH 167/490] Travis: Build with clang too --- .travis.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.travis.yml b/.travis.yml index d59689f78..2eab656cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,10 @@ branches: os: - linux +compiler: + - gcc + - clang + env: global: # COVERITY_SCAN_TOKEN @@ -34,6 +38,10 @@ env: matrix: exclude: - os: linux + compiler: gcc + env: + - os: linux + compiler: clang env: include: @@ -49,6 +57,12 @@ matrix: - os: linux compiler: gcc env: LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF + - os: linux + compiler: clang + env: LINUX=1 TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=ON + - os: linux + compiler: clang + env: LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF install: - if [ $ANDROID ]; then wget -c http://dl.google.com/android/ndk/android-ndk-${PV}-${PLATF}.tar.bz2 && tar xf android-ndk-${PV}-${PLATF}.tar.bz2 ; fi From 5cc316b874ad8976983f5ba89e473ab14c149966 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 4 Oct 2017 21:10:49 +0300 Subject: [PATCH 168/490] Travis: Treat warnings as errors, without typos this time --- .travis.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.sh b/.travis.sh index f19c3a000..1ab1ee2b1 100755 --- a/.travis.sh +++ b/.travis.sh @@ -1,6 +1,6 @@ function generate() { - cmake -G "Unix Makefiles" -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -DBUILD_SHARED_LIBS=$SHARED_BUILD -DASSIMP_COVERALLS=$ENABLE_COVERALLS -DASSIMP_ERROR=ON -DASSIMP_ASAN=$ASAN + cmake -G "Unix Makefiles" -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -DBUILD_SHARED_LIBS=$SHARED_BUILD -DASSIMP_COVERALLS=$ENABLE_COVERALLS -DASSIMP_WERROR=ON -DASSIMP_ASAN=$ASAN } if [ $ANDROID ]; then From d28e88feb772d192dc9c5586eeeea682c3e6a536 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 4 Oct 2017 21:21:36 +0300 Subject: [PATCH 169/490] CMake: Remove OpenMP stuff, it's unused and breaks Travis clang build --- CMakeLists.txt | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c0fe062e..0aae76837 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,19 +151,6 @@ IF(ASSIMP_DOUBLE_PRECISION) ADD_DEFINITIONS(-DASSIMP_DOUBLE_PRECISION) ENDIF(ASSIMP_DOUBLE_PRECISION) -# Check for OpenMP support -find_package(OpenMP) -if (OPENMP_FOUND) - SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - - IF(MSVC) - IF(MSVC_VERSION GREATER 1910) - SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:twoPhase-") - ENDIF() - ENDIF() -endif() - CONFIGURE_FILE( ${CMAKE_CURRENT_LIST_DIR}/revision.h.in ${CMAKE_CURRENT_BINARY_DIR}/revision.h From ae8a4c0c62f43db2126fc7228877346d271a5600 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 10:45:25 +0300 Subject: [PATCH 170/490] Fix warning about non-constant array size --- test/unit/TestModelFactory.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/TestModelFactory.h b/test/unit/TestModelFactory.h index f848f5536..ca070890d 100644 --- a/test/unit/TestModelFactory.h +++ b/test/unit/TestModelFactory.h @@ -89,7 +89,7 @@ public: scene->mRootNode = new aiNode; scene->mRootNode->mNumMeshes = 1; - scene->mRootNode->mMeshes = new unsigned int[scene->mRootNode->mNumMeshes]{ 0 }; + scene->mRootNode->mMeshes = new unsigned int[1]{ 0 }; return scene; } From 9f5b58e706221857cd7010710f00e8d6536eb540 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 11:10:22 +0300 Subject: [PATCH 171/490] Upgrade RapidJSON to get rid of a clang warning --- .../rapidjson/include/rapidjson/allocators.h | 42 +- .../rapidjson/include/rapidjson/document.h | 1073 +++++++-- .../include/rapidjson/encodedstream.h | 58 +- .../rapidjson/include/rapidjson/encodings.h | 233 +- .../rapidjson/include/rapidjson/error/en.h | 21 +- .../rapidjson/include/rapidjson/error/error.h | 27 +- .../include/rapidjson/filereadstream.h | 13 +- .../include/rapidjson/filewritestream.h | 17 +- contrib/rapidjson/include/rapidjson/fwd.h | 151 ++ .../include/rapidjson/internal/biginteger.h | 14 +- .../include/rapidjson/internal/diyfp.h | 19 +- .../include/rapidjson/internal/dtoa.h | 50 +- .../include/rapidjson/internal/ieee754.h | 3 +- .../include/rapidjson/internal/regex.h | 734 ++++++ .../include/rapidjson/internal/stack.h | 68 +- .../include/rapidjson/internal/strfunc.h | 32 +- .../include/rapidjson/internal/strtod.h | 39 +- .../include/rapidjson/internal/swap.h | 9 + .../include/rapidjson/istreamwrapper.h | 115 + .../include/rapidjson/memorybuffer.h | 2 +- .../include/rapidjson/memorystream.h | 16 +- .../include/rapidjson/msinttypes/stdint.h | 8 +- .../include/rapidjson/ostreamwrapper.h | 81 + contrib/rapidjson/include/rapidjson/pointer.h | 131 +- .../include/rapidjson/prettywriter.h | 98 +- .../rapidjson/include/rapidjson/rapidjson.h | 314 ++- contrib/rapidjson/include/rapidjson/reader.h | 1345 ++++++++--- contrib/rapidjson/include/rapidjson/schema.h | 2016 +++++++++++++++++ contrib/rapidjson/include/rapidjson/stream.h | 179 ++ .../include/rapidjson/stringbuffer.h | 30 +- contrib/rapidjson/include/rapidjson/writer.h | 468 +++- contrib/rapidjson/license.txt | 2 +- contrib/rapidjson/readme.md | 81 +- 33 files changed, 6467 insertions(+), 1022 deletions(-) create mode 100644 contrib/rapidjson/include/rapidjson/fwd.h create mode 100644 contrib/rapidjson/include/rapidjson/internal/regex.h create mode 100644 contrib/rapidjson/include/rapidjson/istreamwrapper.h create mode 100644 contrib/rapidjson/include/rapidjson/ostreamwrapper.h create mode 100644 contrib/rapidjson/include/rapidjson/schema.h create mode 100644 contrib/rapidjson/include/rapidjson/stream.h diff --git a/contrib/rapidjson/include/rapidjson/allocators.h b/contrib/rapidjson/include/rapidjson/allocators.h index d74a67155..655f4a385 100644 --- a/contrib/rapidjson/include/rapidjson/allocators.h +++ b/contrib/rapidjson/include/rapidjson/allocators.h @@ -179,9 +179,10 @@ public: size = RAPIDJSON_ALIGN(size); if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) - AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size); + if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) + return NULL; - void *buffer = reinterpret_cast(chunkHead_ + 1) + chunkHead_->size; + void *buffer = reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size; chunkHead_->size += size; return buffer; } @@ -194,14 +195,16 @@ public: if (newSize == 0) return NULL; + originalSize = RAPIDJSON_ALIGN(originalSize); + newSize = RAPIDJSON_ALIGN(newSize); + // Do not shrink if new size is smaller than original if (originalSize >= newSize) return originalPtr; // Simply expand it if it is the last allocation and there is sufficient space - if (originalPtr == (char *)(chunkHead_ + 1) + chunkHead_->size - originalSize) { + if (originalPtr == reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) { size_t increment = static_cast(newSize - originalSize); - increment = RAPIDJSON_ALIGN(increment); if (chunkHead_->size + increment <= chunkHead_->capacity) { chunkHead_->size += increment; return originalPtr; @@ -209,11 +212,13 @@ public: } // Realloc process: allocate and copy memory, do not free original buffer. - void* newBuffer = Malloc(newSize); - RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly. - if (originalSize) - std::memcpy(newBuffer, originalPtr, originalSize); - return newBuffer; + if (void* newBuffer = Malloc(newSize)) { + if (originalSize) + std::memcpy(newBuffer, originalPtr, originalSize); + return newBuffer; + } + else + return NULL; } //! Frees a memory block (concept Allocator) @@ -227,15 +232,20 @@ private: //! Creates a new chunk. /*! \param capacity Capacity of the chunk in bytes. + \return true if success. */ - void AddChunk(size_t capacity) { + bool AddChunk(size_t capacity) { if (!baseAllocator_) - ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator()); - ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(sizeof(ChunkHeader) + capacity)); - chunk->capacity = capacity; - chunk->size = 0; - chunk->next = chunkHead_; - chunkHead_ = chunk; + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)(); + if (ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) { + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = chunkHead_; + chunkHead_ = chunk; + return true; + } + else + return false; } static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity. diff --git a/contrib/rapidjson/include/rapidjson/document.h b/contrib/rapidjson/include/rapidjson/document.h index c6acbd907..93b091f64 100644 --- a/contrib/rapidjson/include/rapidjson/document.h +++ b/contrib/rapidjson/include/rapidjson/document.h @@ -20,40 +20,29 @@ #include "reader.h" #include "internal/meta.h" #include "internal/strfunc.h" +#include "memorystream.h" +#include "encodedstream.h" #include // placement new +#include +RAPIDJSON_DIAG_PUSH #ifdef _MSC_VER -RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant -#elif defined(__GNUC__) -RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +#ifdef __GNUC__ RAPIDJSON_DIAG_OFF(effc++) +#if __GNUC__ >= 6 +RAPIDJSON_DIAG_OFF(terminate) // ignore throwing RAPIDJSON_ASSERT in RAPIDJSON_NOEXCEPT functions #endif - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_HAS_STDSTRING - -#ifndef RAPIDJSON_HAS_STDSTRING -#ifdef RAPIDJSON_DOXYGEN_RUNNING -#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation -#else -#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default -#endif -/*! \def RAPIDJSON_HAS_STDSTRING - \ingroup RAPIDJSON_CONFIG - \brief Enable RapidJSON support for \c std::string - - By defining this preprocessor symbol to \c 1, several convenience functions for using - \ref rapidjson::GenericValue with \c std::string are enabled, especially - for construction and comparison. - - \hideinitializer -*/ -#endif // !defined(RAPIDJSON_HAS_STDSTRING) - -#if RAPIDJSON_HAS_STDSTRING -#include -#endif // RAPIDJSON_HAS_STDSTRING +#endif // __GNUC__ #ifndef RAPIDJSON_NOMEMBERITERATORCLASS #include // std::iterator, std::random_access_iterator_tag @@ -69,6 +58,9 @@ RAPIDJSON_NAMESPACE_BEGIN template class GenericValue; +template +class GenericDocument; + //! Name-value pair in a JSON object value. /*! This class was internal to GenericValue. It used to be a inner struct. @@ -155,6 +147,7 @@ public: Otherwise, the copy constructor is implicitly defined. */ GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} + Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; } //! @name stepping //@{ @@ -257,6 +250,7 @@ struct GenericStringRef { typedef CharType Ch; //!< character type of the string //! Create string reference from \c const character array +#ifndef __clang__ // -Wdocumentation /*! This constructor implicitly creates a constant string reference from a \c const character array. It has better performance than @@ -279,11 +273,13 @@ struct GenericStringRef { In such cases, the referenced string should be \b copied to the GenericValue instead. */ +#endif template GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT : s(str), length(N-1) {} //! Explicitly create string reference from \c const character pointer +#ifndef __clang__ // -Wdocumentation /*! This constructor can be used to \b explicitly create a reference to a constant string pointer. @@ -302,18 +298,23 @@ struct GenericStringRef { In such cases, the referenced string should be \b copied to the GenericValue instead. */ +#endif explicit GenericStringRef(const CharType* str) - : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != NULL); } + : s(str), length(NotNullStrLen(str)) {} //! Create constant string reference from pointer and length +#ifndef __clang__ // -Wdocumentation /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue \param len length of the string, excluding the trailing NULL terminator \post \ref s == str && \ref length == len \note Constant complexity. */ +#endif GenericStringRef(const CharType* str, SizeType len) - : s(str), length(len) { RAPIDJSON_ASSERT(s != NULL); } + : s(RAPIDJSON_LIKELY(str) ? str : emptyString), length(len) { RAPIDJSON_ASSERT(str != 0 || len == 0u); } + + GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} //! implicit conversion to plain CharType pointer operator const Ch *() const { return s; } @@ -322,13 +323,24 @@ struct GenericStringRef { const SizeType length; //!< length of the string (excluding the trailing NULL terminator) private: - //! Disallow copy-assignment - GenericStringRef operator=(const GenericStringRef&); + SizeType NotNullStrLen(const CharType* str) { + RAPIDJSON_ASSERT(str != 0); + return internal::StrLen(str); + } + + /// Empty string - used when passing in a NULL pointer + static const Ch emptyString[]; + //! Disallow construction from non-const array template GenericStringRef(CharType (&str)[N]) /* = delete */; + //! Copy assignment operator not permitted - immutable type + GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; }; +template +const CharType GenericStringRef::emptyString[] = { CharType() }; + //! Mark a character pointer as constant string /*! Mark a plain character pointer as a "string literal". This function can be used to avoid copying a character string to be referenced as a @@ -343,7 +355,7 @@ private: */ template inline GenericStringRef StringRef(const CharType* str) { - return GenericStringRef(str, internal::StrLen(str)); + return GenericStringRef(str); } //! Mark a character pointer as constant string @@ -401,6 +413,127 @@ template struct IsGenericValue : IsGenericValueImpl::Type {}; } // namespace internal +/////////////////////////////////////////////////////////////////////////////// +// TypeHelper + +namespace internal { + +template +struct TypeHelper {}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsBool(); } + static bool Get(const ValueType& v) { return v.GetBool(); } + static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); } + static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static int Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt64(); } + static int64_t Get(const ValueType& v) { return v.GetInt64(); } + static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); } + static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint64(); } + static uint64_t Get(const ValueType& v) { return v.GetUint64(); } + static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); } + static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsDouble(); } + static double Get(const ValueType& v) { return v.GetDouble(); } + static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); } + static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsFloat(); } + static float Get(const ValueType& v) { return v.GetFloat(); } + static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); } + static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); } +}; + +template +struct TypeHelper { + typedef const typename ValueType::Ch* StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return v.GetString(); } + static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); } + static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; + +#if RAPIDJSON_HAS_STDSTRING +template +struct TypeHelper > { + typedef std::basic_string StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return StringType(v.GetString(), v.GetStringLength()); } + static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; +#endif + +template +struct TypeHelper { + typedef typename ValueType::Array ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(ValueType& v) { return v.GetArray(); } + static ValueType& Set(ValueType& v, ArrayType data) { return v = data; } + static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstArray ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(const ValueType& v) { return v.GetArray(); } +}; + +template +struct TypeHelper { + typedef typename ValueType::Object ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(ValueType& v) { return v.GetObject(); } + static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } + static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstObject ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(const ValueType& v) { return v.GetObject(); } +}; + +} // namespace internal + +// Forward declarations +template class GenericArray; +template class GenericObject; + /////////////////////////////////////////////////////////////////////////////// // GenericValue @@ -428,17 +561,21 @@ public: typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. typedef GenericValue ValueType; //!< Value type of itself. + typedef GenericArray Array; + typedef GenericArray ConstArray; + typedef GenericObject Object; + typedef GenericObject ConstObject; //!@name Constructors and destructor. //@{ //! Default constructor creates a null value. - GenericValue() RAPIDJSON_NOEXCEPT : data_(), flags_(kNullFlag) {} + GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Move constructor in C++11 - GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_), flags_(rhs.flags_) { - rhs.flags_ = kNullFlag; // give up contents + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) { + rhs.data_.f.flags = kNullFlag; // give up contents } #endif @@ -446,6 +583,16 @@ private: //! Copy constructor is not permitted. GenericValue(const GenericValue& rhs); +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Moving from a GenericDocument is not permitted. + template + GenericValue(GenericDocument&& rhs); + + //! Move assignment from a GenericDocument is not permitted. + template + GenericValue& operator=(GenericDocument&& rhs); +#endif + public: //! Constructor with JSON value type. @@ -453,13 +600,13 @@ public: \param type Type of the value. \note Default content for number is zero. */ - explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_(), flags_() { - static const unsigned defaultFlags[7] = { + explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() { + static const uint16_t defaultFlags[7] = { kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, kNumberAnyFlag }; - RAPIDJSON_ASSERT(type <= kNumberType); - flags_ = defaultFlags[type]; + RAPIDJSON_ASSERT(type >= kNullType && type <= kNumberType); + data_.f.flags = defaultFlags[type]; // Use ShortString to store empty string. if (type == kStringType) @@ -471,10 +618,50 @@ public: \tparam SourceAllocator allocator of \c rhs \param rhs Value to copy from (read-only) \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) \see CopyFrom() */ - template< typename SourceAllocator > - GenericValue(const GenericValue& rhs, Allocator & allocator); + template + GenericValue(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + switch (rhs.GetType()) { + case kObjectType: { + SizeType count = rhs.data_.o.size; + Member* lm = reinterpret_cast(allocator.Malloc(count * sizeof(Member))); + const typename GenericValue::Member* rm = rhs.GetMembersPointer(); + for (SizeType i = 0; i < count; i++) { + new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings); + new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings); + } + data_.f.flags = kObjectFlag; + data_.o.size = data_.o.capacity = count; + SetMembersPointer(lm); + } + break; + case kArrayType: { + SizeType count = rhs.data_.a.size; + GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); + const GenericValue* re = rhs.GetElementsPointer(); + for (SizeType i = 0; i < count; i++) + new (&le[i]) GenericValue(re[i], allocator, copyConstStrings); + data_.f.flags = kArrayFlag; + data_.a.size = data_.a.capacity = count; + SetElementsPointer(le); + } + break; + case kStringType: + if (rhs.data_.f.flags == kConstStringFlag && !copyConstStrings) { + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + } + else + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + break; + default: + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + break; + } + } //! Constructor for boolean value. /*! \param b Boolean value @@ -484,96 +671,125 @@ public: */ #ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen template - explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT + explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT // See #472 #else explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT #endif - : data_(), flags_(b ? kTrueFlag : kFalseFlag) { + : data_() { // safe-guard against failing SFINAE RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + data_.f.flags = b ? kTrueFlag : kFalseFlag; } //! Constructor for int value. - explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberIntFlag) { + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() { data_.n.i64 = i; - if (i >= 0) - flags_ |= kUintFlag | kUint64Flag; + data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag; } //! Constructor for unsigned value. - explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUintFlag) { + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() { data_.n.u64 = u; - if (!(u & 0x80000000)) - flags_ |= kIntFlag | kInt64Flag; + data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag); } //! Constructor for int64_t value. - explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberInt64Flag) { + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() { data_.n.i64 = i64; + data_.f.flags = kNumberInt64Flag; if (i64 >= 0) { - flags_ |= kNumberUint64Flag; + data_.f.flags |= kNumberUint64Flag; if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) - flags_ |= kUintFlag; + data_.f.flags |= kUintFlag; if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) - flags_ |= kIntFlag; + data_.f.flags |= kIntFlag; } else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) - flags_ |= kIntFlag; + data_.f.flags |= kIntFlag; } //! Constructor for uint64_t value. - explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUint64Flag) { + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() { data_.n.u64 = u64; + data_.f.flags = kNumberUint64Flag; if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) - flags_ |= kInt64Flag; + data_.f.flags |= kInt64Flag; if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) - flags_ |= kUintFlag; + data_.f.flags |= kUintFlag; if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) - flags_ |= kIntFlag; + data_.f.flags |= kIntFlag; } //! Constructor for double value. - explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; } + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for float value. + explicit GenericValue(float f) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = static_cast(f); data_.f.flags = kNumberDoubleFlag; } //! Constructor for constant string (i.e. do not make a copy of string) - GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(StringRef(s, length)); } + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); } //! Constructor for constant string (i.e. do not make a copy of string) - explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(s); } + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); } //! Constructor for copy-string (i.e. do make a copy of string) - GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s, length), allocator); } + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); } //! Constructor for copy-string (i.e. do make a copy of string) - GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } + GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } #if RAPIDJSON_HAS_STDSTRING //! Constructor for copy-string from a string object (i.e. do make a copy of string) /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. */ - GenericValue(const std::basic_string& s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } + GenericValue(const std::basic_string& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } #endif + //! Constructor for Array. + /*! + \param a An array obtained by \c GetArray(). + \note \c Array is always pass-by-value. + \note the source array is moved into this value and the sourec array becomes empty. + */ + GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) { + a.value_.data_ = Data(); + a.value_.data_.f.flags = kArrayFlag; + } + + //! Constructor for Object. + /*! + \param o An object obtained by \c GetObject(). + \note \c Object is always pass-by-value. + \note the source object is moved into this value and the sourec object becomes empty. + */ + GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) { + o.value_.data_ = Data(); + o.value_.data_.f.flags = kObjectFlag; + } + //! Destructor. /*! Need to destruct elements of array, members of object, or copy-string. */ ~GenericValue() { if (Allocator::kNeedFree) { // Shortcut by Allocator's trait - switch(flags_) { + switch(data_.f.flags) { case kArrayFlag: - for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) - v->~GenericValue(); - Allocator::Free(data_.a.elements); + { + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + Allocator::Free(e); + } break; case kObjectFlag: for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) m->~Member(); - Allocator::Free(data_.o.members); + Allocator::Free(GetMembersPointer()); break; case kCopyStringFlag: - Allocator::Free(const_cast(data_.s.str)); + Allocator::Free(const_cast(GetStringPointer())); break; default: @@ -638,12 +854,13 @@ public: \tparam SourceAllocator Allocator type of \c rhs \param rhs Value to copy from (read-only) \param allocator Allocator to use for copying + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) */ template - GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator) { - RAPIDJSON_ASSERT((void*)this != (void const*)&rhs); + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); this->~GenericValue(); - new (this) GenericValue(rhs, allocator); + new (this) GenericValue(rhs, allocator, copyConstStrings); return *this; } @@ -660,6 +877,20 @@ public: return *this; } + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.value, b.value); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericValue& a, GenericValue& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + //! Prepare Value for move semantics /*! \return *this */ GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } @@ -709,7 +940,7 @@ public: else return data_.n.u64 == rhs.data_.n.u64; - default: // kTrueType, kFalseType, kNullType + default: return true; } } @@ -757,20 +988,58 @@ public: //!@name Type //@{ - Type GetType() const { return static_cast(flags_ & kTypeMask); } - bool IsNull() const { return flags_ == kNullFlag; } - bool IsFalse() const { return flags_ == kFalseFlag; } - bool IsTrue() const { return flags_ == kTrueFlag; } - bool IsBool() const { return (flags_ & kBoolFlag) != 0; } - bool IsObject() const { return flags_ == kObjectFlag; } - bool IsArray() const { return flags_ == kArrayFlag; } - bool IsNumber() const { return (flags_ & kNumberFlag) != 0; } - bool IsInt() const { return (flags_ & kIntFlag) != 0; } - bool IsUint() const { return (flags_ & kUintFlag) != 0; } - bool IsInt64() const { return (flags_ & kInt64Flag) != 0; } - bool IsUint64() const { return (flags_ & kUint64Flag) != 0; } - bool IsDouble() const { return (flags_ & kDoubleFlag) != 0; } - bool IsString() const { return (flags_ & kStringFlag) != 0; } + Type GetType() const { return static_cast(data_.f.flags & kTypeMask); } + bool IsNull() const { return data_.f.flags == kNullFlag; } + bool IsFalse() const { return data_.f.flags == kFalseFlag; } + bool IsTrue() const { return data_.f.flags == kTrueFlag; } + bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; } + bool IsObject() const { return data_.f.flags == kObjectFlag; } + bool IsArray() const { return data_.f.flags == kArrayFlag; } + bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; } + bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; } + bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; } + bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; } + bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; } + bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; } + bool IsString() const { return (data_.f.flags & kStringFlag) != 0; } + + // Checks whether a number can be losslessly converted to a double. + bool IsLosslessDouble() const { + if (!IsNumber()) return false; + if (IsUint64()) { + uint64_t u = GetUint64(); + volatile double d = static_cast(u); + return (d >= 0.0) + && (d < static_cast((std::numeric_limits::max)())) + && (u == static_cast(d)); + } + if (IsInt64()) { + int64_t i = GetInt64(); + volatile double d = static_cast(i); + return (d >= static_cast((std::numeric_limits::min)())) + && (d < static_cast((std::numeric_limits::max)())) + && (i == static_cast(d)); + } + return true; // double, int, uint are always lossless + } + + // Checks whether a number is a float (possible lossy). + bool IsFloat() const { + if ((data_.f.flags & kDoubleFlag) == 0) + return false; + double d = GetDouble(); + return d >= -3.4028234e38 && d <= 3.4028234e38; + } + // Checks whether a number can be losslessly converted to a float. + bool IsLosslessFloat() const { + if (!IsNumber()) return false; + double a = GetDouble(); + if (a < static_cast(-(std::numeric_limits::max)()) + || a > static_cast((std::numeric_limits::max)())) + return false; + double b = static_cast(static_cast(a)); + return a >= b && a <= b; // Prevent -Wfloat-equal + } //@} @@ -784,7 +1053,7 @@ public: //!@name Bool //@{ - bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return flags_ == kTrueFlag; } + bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; } //!< Set boolean value /*! \post IsBool() == true */ GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } @@ -837,8 +1106,14 @@ public: return member->value; else { RAPIDJSON_ASSERT(false); // see above note - static GenericValue NullValue; - return NullValue; + + // This will generate -Wexit-time-destructors in clang + // static GenericValue NullValue; + // return NullValue; + + // Use static buffer and placement-new to prevent destruction + static char buffer[sizeof(GenericValue)]; + return *new (buffer) GenericValue(); } } template @@ -852,16 +1127,16 @@ public: //! Const member iterator /*! \pre IsObject() == true */ - ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members); } + ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); } //! Const \em past-the-end member iterator /*! \pre IsObject() == true */ - ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members + data_.o.size); } + ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); } //! Member iterator /*! \pre IsObject() == true */ - MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members); } + MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); } //! \em Past-the-end member iterator /*! \pre IsObject() == true */ - MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members + data_.o.size); } + MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } //! Check whether a member exists in the object. /*! @@ -949,8 +1224,8 @@ public: \return Iterator to member, if it exists. Otherwise returns \ref MemberEnd(). */ - MemberIterator FindMember(const std::basic_string& name) { return FindMember(StringRef(name)); } - ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(StringRef(name)); } + MemberIterator FindMember(const std::basic_string& name) { return FindMember(GenericValue(StringRef(name))); } + ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(GenericValue(StringRef(name))); } #endif //! Add a member (name-value pair) to the object. @@ -967,20 +1242,21 @@ public: RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(name.IsString()); - Object& o = data_.o; + ObjectData& o = data_.o; if (o.size >= o.capacity) { if (o.capacity == 0) { o.capacity = kDefaultObjectCapacity; - o.members = reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member))); + SetMembersPointer(reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member)))); } else { SizeType oldCapacity = o.capacity; o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5 - o.members = reinterpret_cast(allocator.Realloc(o.members, oldCapacity * sizeof(Member), o.capacity * sizeof(Member))); + SetMembersPointer(reinterpret_cast(allocator.Realloc(GetMembersPointer(), oldCapacity * sizeof(Member), o.capacity * sizeof(Member)))); } } - o.members[o.size].name.RawAssign(name); - o.members[o.size].value.RawAssign(value); + Member* members = GetMembersPointer(); + members[o.size].name.RawAssign(name); + members[o.size].value.RawAssign(value); o.size++; return *this; } @@ -1159,18 +1435,14 @@ public: MemberIterator RemoveMember(MemberIterator m) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(data_.o.size > 0); - RAPIDJSON_ASSERT(data_.o.members != 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); - MemberIterator last(data_.o.members + (data_.o.size - 1)); - if (data_.o.size > 1 && m != last) { - // Move the last one to this place - *m = *last; - } - else { - // Only one left, just destroy - m->~Member(); - } + MemberIterator last(GetMembersPointer() + (data_.o.size - 1)); + if (data_.o.size > 1 && m != last) + *m = *last; // Move the last one to this place + else + m->~Member(); // Only one left, just destroy --data_.o.size; return m; } @@ -1200,7 +1472,7 @@ public: MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(data_.o.size > 0); - RAPIDJSON_ASSERT(data_.o.members != 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); RAPIDJSON_ASSERT(first >= MemberBegin()); RAPIDJSON_ASSERT(first <= last); RAPIDJSON_ASSERT(last <= MemberEnd()); @@ -1208,11 +1480,39 @@ public: MemberIterator pos = MemberBegin() + (first - MemberBegin()); for (MemberIterator itr = pos; itr != last; ++itr) itr->~Member(); - std::memmove(&*pos, &*last, (MemberEnd() - last) * sizeof(Member)); - data_.o.size -= (last - first); + std::memmove(&*pos, &*last, static_cast(MemberEnd() - last) * sizeof(Member)); + data_.o.size -= static_cast(last - first); return pos; } + //! Erase a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note Linear time complexity. + */ + bool EraseMember(const Ch* name) { + GenericValue n(StringRef(name)); + return EraseMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) { return EraseMember(GenericValue(StringRef(name))); } +#endif + + template + bool EraseMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + EraseMember(m); + return true; + } + else + return false; + } + + Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } + ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } + //@} //!@name Array @@ -1220,7 +1520,7 @@ public: //! Set this value as an empty array. /*! \post IsArray == true */ - GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } + GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } //! Get the number of elements in array. SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } @@ -1237,8 +1537,9 @@ public: */ void Clear() { RAPIDJSON_ASSERT(IsArray()); - for (SizeType i = 0; i < data_.a.size; ++i) - data_.a.elements[i].~GenericValue(); + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); data_.a.size = 0; } @@ -1250,16 +1551,16 @@ public: GenericValue& operator[](SizeType index) { RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(index < data_.a.size); - return data_.a.elements[index]; + return GetElementsPointer()[index]; } const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } //! Element iterator /*! \pre IsArray() == true */ - ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements; } + ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); } //! \em Past-the-end element iterator /*! \pre IsArray() == true */ - ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements + data_.a.size; } + ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; } //! Constant element iterator /*! \pre IsArray() == true */ ConstValueIterator Begin() const { return const_cast(*this).Begin(); } @@ -1276,7 +1577,7 @@ public: GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { RAPIDJSON_ASSERT(IsArray()); if (newCapacity > data_.a.capacity) { - data_.a.elements = (GenericValue*)allocator.Realloc(data_.a.elements, data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)); + SetElementsPointer(reinterpret_cast(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)))); data_.a.capacity = newCapacity; } return *this; @@ -1296,7 +1597,7 @@ public: RAPIDJSON_ASSERT(IsArray()); if (data_.a.size >= data_.a.capacity) Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); - data_.a.elements[data_.a.size++].RawAssign(value); + GetElementsPointer()[data_.a.size++].RawAssign(value); return *this; } @@ -1350,7 +1651,7 @@ public: GenericValue& PopBack() { RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(!Empty()); - data_.a.elements[--data_.a.size].~GenericValue(); + GetElementsPointer()[--data_.a.size].~GenericValue(); return *this; } @@ -1376,35 +1677,48 @@ public: ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(data_.a.size > 0); - RAPIDJSON_ASSERT(data_.a.elements != 0); + RAPIDJSON_ASSERT(GetElementsPointer() != 0); RAPIDJSON_ASSERT(first >= Begin()); RAPIDJSON_ASSERT(first <= last); RAPIDJSON_ASSERT(last <= End()); ValueIterator pos = Begin() + (first - Begin()); for (ValueIterator itr = pos; itr != last; ++itr) itr->~GenericValue(); - std::memmove(pos, last, (End() - last) * sizeof(GenericValue)); - data_.a.size -= (last - first); + std::memmove(pos, last, static_cast(End() - last) * sizeof(GenericValue)); + data_.a.size -= static_cast(last - first); return pos; } + Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); } + ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); } + //@} //!@name Number //@{ - int GetInt() const { RAPIDJSON_ASSERT(flags_ & kIntFlag); return data_.n.i.i; } - unsigned GetUint() const { RAPIDJSON_ASSERT(flags_ & kUintFlag); return data_.n.u.u; } - int64_t GetInt64() const { RAPIDJSON_ASSERT(flags_ & kInt64Flag); return data_.n.i64; } - uint64_t GetUint64() const { RAPIDJSON_ASSERT(flags_ & kUint64Flag); return data_.n.u64; } + int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; } + //! Get the value as double type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless. + */ double GetDouble() const { RAPIDJSON_ASSERT(IsNumber()); - if ((flags_ & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. - if ((flags_ & kIntFlag) != 0) return data_.n.i.i; // int -> double - if ((flags_ & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double - if ((flags_ & kInt64Flag) != 0) return (double)data_.n.i64; // int64_t -> double (may lose precision) - RAPIDJSON_ASSERT((flags_ & kUint64Flag) != 0); return (double)data_.n.u64; // uint64_t -> double (may lose precision) + if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((data_.f.flags & kInt64Flag) != 0) return static_cast(data_.n.i64); // int64_t -> double (may lose precision) + RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast(data_.n.u64); // uint64_t -> double (may lose precision) + } + + //! Get the value as float type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless. + */ + float GetFloat() const { + return static_cast(GetDouble()); } GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } @@ -1412,18 +1726,19 @@ public: GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } + GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(static_cast(f)); return *this; } //@} //!@name String //@{ - const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? data_.ss.str : data_.s.str); } + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); } //! Get the length of string. /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). */ - SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } //! Set this value as a string without copying source string. /*! This version has better performance with supplied length, and also support string containing null character. @@ -1450,7 +1765,7 @@ public: \return The value itself for fluent API. \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length */ - GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; } + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { return SetString(StringRef(s, length), allocator); } //! Set this value as a string by copying from source string. /*! \param s source string. @@ -1458,7 +1773,15 @@ public: \return The value itself for fluent API. \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length */ - GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(StringRef(s), allocator); } + + //! Set this value as a string by copying from source string. + /*! \param s source string reference + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(StringRefType s, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, allocator); return *this; } #if RAPIDJSON_HAS_STDSTRING //! Set this value as a string by copying from source string. @@ -1468,11 +1791,35 @@ public: \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. */ - GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(s.data(), SizeType(s.size()), allocator); } + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(StringRef(s), allocator); } #endif //@} + //!@name Array + //@{ + + //! Templated version for checking whether this value is type T. + /*! + \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string + */ + template + bool Is() const { return internal::TypeHelper::Is(*this); } + + template + T Get() const { return internal::TypeHelper::Get(*this); } + + template + T Get() { return internal::TypeHelper::Get(*this); } + + template + ValueType& Set(const T& data) { return internal::TypeHelper::Set(*this, data); } + + template + ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper::Set(*this, data, allocator); } + + //@} + //! Generate events of this value to a Handler. /*! This function adopts the GoF visitor pattern. Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. @@ -1488,35 +1835,35 @@ public: case kTrueType: return handler.Bool(true); case kObjectType: - if (!handler.StartObject()) + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) return false; for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. - if (!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.flags_ & kCopyFlag) != 0)) + if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0))) return false; - if (!m->value.Accept(handler)) + if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler))) return false; } return handler.EndObject(data_.o.size); case kArrayType: - if (!handler.StartArray()) + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) return false; - for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) - if (!v->Accept(handler)) + for (const GenericValue* v = Begin(); v != End(); ++v) + if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) return false; return handler.EndArray(data_.a.size); case kStringType: - return handler.String(GetString(), GetStringLength(), (flags_ & kCopyFlag) != 0); + return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0); default: RAPIDJSON_ASSERT(GetType() == kNumberType); - if (IsInt()) return handler.Int(data_.n.i.i); + if (IsDouble()) return handler.Double(data_.n.d); + else if (IsInt()) return handler.Int(data_.n.i.i); else if (IsUint()) return handler.Uint(data_.n.u.u); else if (IsInt64()) return handler.Int64(data_.n.i64); - else if (IsUint64()) return handler.Uint64(data_.n.u64); - else return handler.Double(data_.n.d); + else return handler.Uint64(data_.n.u64); } } @@ -1525,16 +1872,16 @@ private: template friend class GenericDocument; enum { - kBoolFlag = 0x100, - kNumberFlag = 0x200, - kIntFlag = 0x400, - kUintFlag = 0x800, - kInt64Flag = 0x1000, - kUint64Flag = 0x2000, - kDoubleFlag = 0x4000, - kStringFlag = 0x100000, - kCopyFlag = 0x200000, - kInlineStrFlag = 0x400000, + kBoolFlag = 0x0008, + kNumberFlag = 0x0010, + kIntFlag = 0x0020, + kUintFlag = 0x0040, + kInt64Flag = 0x0080, + kUint64Flag = 0x0100, + kDoubleFlag = 0x0200, + kStringFlag = 0x0400, + kCopyFlag = 0x0800, + kInlineStrFlag = 0x1000, // Initial flags of different types. kNullFlag = kNullType, @@ -1552,16 +1899,27 @@ private: kObjectFlag = kObjectType, kArrayFlag = kArrayType, - kTypeMask = 0xFF // bitwise-and with mask of 0xFF can be optimized by compiler + kTypeMask = 0x07 }; static const SizeType kDefaultArrayCapacity = 16; static const SizeType kDefaultObjectCapacity = 16; + struct Flag { +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION + char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer +#elif RAPIDJSON_64BIT + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes +#else + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes +#endif + uint16_t flags; + }; + struct String { - const Ch* str; SizeType length; - unsigned hashcode; //!< reserved + SizeType hashcode; //!< reserved + const Ch* str; }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars @@ -1570,15 +1928,15 @@ private: // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as // the string terminator as well. For getting the string length back from that value just use // "MaxSize - str[LenPos]". - // This allows to store 11-chars strings in 32-bit mode and 15-chars strings in 64-bit mode - // inline (for `UTF8`-encoded strings). + // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode, + // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings). struct ShortString { - enum { MaxChars = sizeof(String) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + enum { MaxChars = sizeof(static_cast(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; Ch str[MaxChars]; - inline static bool Usable(SizeType len) { return (MaxSize >= len); } - inline void SetLength(SizeType len) { str[LenPos] = (Ch)(MaxSize - len); } - inline SizeType GetLength() const { return (SizeType)(MaxSize - str[LenPos]); } + inline static bool Usable(SizeType len) { return (MaxSize >= len); } + inline void SetLength(SizeType len) { str[LenPos] = static_cast(MaxSize - len); } + inline SizeType GetLength() const { return static_cast(MaxSize - str[LenPos]); } }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode // By using proper binary layout, retrieval of different integer types do not need conversions. @@ -1607,69 +1965,79 @@ private: double d; }; // 8 bytes - struct Object { - Member* members; + struct ObjectData { SizeType size; SizeType capacity; + Member* members; }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode - struct Array { - GenericValue* elements; + struct ArrayData { SizeType size; SizeType capacity; + GenericValue* elements; }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode union Data { String s; ShortString ss; Number n; - Object o; - Array a; - }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + ObjectData o; + ArrayData a; + Flag f; + }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION + + RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); } + RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); } + RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); } + RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } + RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); } + RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } // Initialize this value as array with initial data, without calling destructor. void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { - flags_ = kArrayFlag; + data_.f.flags = kArrayFlag; if (count) { - data_.a.elements = (GenericValue*)allocator.Malloc(count * sizeof(GenericValue)); - std::memcpy(data_.a.elements, values, count * sizeof(GenericValue)); + GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); + SetElementsPointer(e); + std::memcpy(e, values, count * sizeof(GenericValue)); } else - data_.a.elements = NULL; + SetElementsPointer(0); data_.a.size = data_.a.capacity = count; } //! Initialize this value as object with initial data, without calling destructor. void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { - flags_ = kObjectFlag; + data_.f.flags = kObjectFlag; if (count) { - data_.o.members = (Member*)allocator.Malloc(count * sizeof(Member)); - std::memcpy(data_.o.members, members, count * sizeof(Member)); + Member* m = static_cast(allocator.Malloc(count * sizeof(Member))); + SetMembersPointer(m); + std::memcpy(m, members, count * sizeof(Member)); } else - data_.o.members = NULL; + SetMembersPointer(0); data_.o.size = data_.o.capacity = count; } //! Initialize this value as constant string, without calling destructor. void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { - flags_ = kConstStringFlag; - data_.s.str = s; + data_.f.flags = kConstStringFlag; + SetStringPointer(s); data_.s.length = s.length; } //! Initialize this value as copy string with initial data, without calling destructor. void SetStringRaw(StringRefType s, Allocator& allocator) { - Ch* str = NULL; - if(ShortString::Usable(s.length)) { - flags_ = kShortStringFlag; + Ch* str = 0; + if (ShortString::Usable(s.length)) { + data_.f.flags = kShortStringFlag; data_.ss.SetLength(s.length); str = data_.ss.str; } else { - flags_ = kCopyStringFlag; + data_.f.flags = kCopyStringFlag; data_.s.length = s.length; - str = (Ch *)allocator.Malloc((s.length + 1) * sizeof(Ch)); - data_.s.str = str; + str = static_cast(allocator.Malloc((s.length + 1) * sizeof(Ch))); + SetStringPointer(str); } std::memcpy(str, s, s.length * sizeof(Ch)); str[s.length] = '\0'; @@ -1678,8 +2046,8 @@ private: //! Assignment without calling destructor void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { data_ = rhs.data_; - flags_ = rhs.flags_; - rhs.flags_ = kNullFlag; + // data_.f.flags = rhs.data_.f.flags; + rhs.data_.f.flags = kNullFlag; } template @@ -1699,7 +2067,6 @@ private: } Data data_; - unsigned flags_; }; //! GenericValue with UTF8 encoding @@ -1724,7 +2091,22 @@ public: typedef Allocator AllocatorType; //!< Allocator type from template parameter. //! Constructor - /*! \param allocator Optional allocator for allocating memory. + /*! Creates an empty document of specified type. + \param type Mandatory type of object to create. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + } + + //! Constructor + /*! Creates an empty document which type is Null. + \param allocator Optional allocator for allocating memory. \param stackCapacity Optional initial capacity of stack in bytes. \param stackAllocator Optional allocator for allocating memory for stack. */ @@ -1732,13 +2114,13 @@ public: allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Move constructor in C++11 GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT - : ValueType(std::move(rhs)), + : ValueType(std::forward(rhs)), // explicit cast to avoid prohibited move from Document allocator_(rhs.allocator_), ownAllocator_(rhs.ownAllocator_), stack_(std::move(rhs.stack_)), @@ -1778,6 +2160,54 @@ public: } #endif + //! Exchange the contents of this document with those of another. + /*! + \param rhs Another document. + \note Constant complexity. + \see GenericValue::Swap + */ + GenericDocument& Swap(GenericDocument& rhs) RAPIDJSON_NOEXCEPT { + ValueType::Swap(rhs); + stack_.Swap(rhs.stack_); + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(parseResult_, rhs.parseResult_); + return *this; + } + + // Allow Swap with ValueType. + // Refer to Effective C++ 3rd Edition/Item 33: Avoid hiding inherited names. + using ValueType::Swap; + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.doc, b.doc); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Populate this document by a generator which produces SAX events. + /*! \tparam Generator A functor with bool f(Handler) prototype. + \param g Generator functor which sends SAX events to the parameter. + \return The document itself for fluent API. + */ + template + GenericDocument& Populate(Generator& g) { + ClearStackOnExit scope(*this); + if (g(*this)) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + //!@name Parse from stream //!@{ @@ -1790,13 +2220,13 @@ public: */ template GenericDocument& ParseStream(InputStream& is) { - ValueType::SetNull(); // Remove existing root if exist - GenericReader reader(&stack_.GetAllocator()); + GenericReader reader( + stack_.HasAllocator() ? &stack_.GetAllocator() : 0); ClearStackOnExit scope(*this); parseResult_ = reader.template Parse(is, *this); if (parseResult_) { RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object - this->RawAssign(*stack_.template Pop(1)); // Add this-> to prevent issue 13. + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document } return *this; } @@ -1855,7 +2285,7 @@ public: \param str Read-only zero-terminated string to be parsed. */ template - GenericDocument& Parse(const Ch* str) { + GenericDocument& Parse(const typename SourceEncoding::Ch* str) { RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); GenericStringStream s(str); return ParseStream(s); @@ -1876,6 +2306,42 @@ public: GenericDocument& Parse(const Ch* str) { return Parse(str); } + + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + MemoryStream ms(reinterpret_cast(str), length * sizeof(typename SourceEncoding::Ch)); + EncodedInputStream is(ms); + ParseStream(is); + return *this; + } + + template + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + +#if RAPIDJSON_HAS_STDSTRING + template + GenericDocument& Parse(const std::basic_string& str) { + // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t) + return Parse(str.c_str()); + } + + template + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str.c_str()); + } + + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str); + } +#endif // RAPIDJSON_HAS_STDSTRING + //!@} //!@name Handling parse errors @@ -1890,10 +2356,26 @@ public: //! Get the position of last parsing error in input, 0 otherwise. size_t GetErrorOffset() const { return parseResult_.Offset(); } + //! Implicit conversion to get the last parse result +#ifndef __clang // -Wdocumentation + /*! \return \ref ParseResult of the last parse operation + + \code + Document doc; + ParseResult ok = doc.Parse(json); + if (!ok) + printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset()); + \endcode + */ +#endif + operator ParseResult() const { return parseResult_; } //!@} //! Get the allocator of this document. - Allocator& GetAllocator() { return *allocator_; } + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } //! Get the capacity of stack in bytes. size_t GetStackCapacity() const { return stack_.GetCapacity(); } @@ -1910,9 +2392,10 @@ private: }; // callers of the following private Handler functions - template friend class GenericReader; // for parsing + // template friend class GenericReader; // for parsing template friend class GenericValue; // for deep copying +public: // Implementation of Handler bool Null() { new (stack_.template Push()) ValueType(); return true; } bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } @@ -1922,6 +2405,14 @@ private: bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } + bool RawNumber(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + bool String(const Ch* str, SizeType length, bool copy) { if (copy) new (stack_.template Push()) ValueType(str, length, GetAllocator()); @@ -1936,7 +2427,7 @@ private: bool EndObject(SizeType memberCount) { typename ValueType::Member* members = stack_.template Pop(memberCount); - stack_.template Top()->SetObjectRaw(members, (SizeType)memberCount, GetAllocator()); + stack_.template Top()->SetObjectRaw(members, memberCount, GetAllocator()); return true; } @@ -1977,38 +2468,146 @@ private: //! GenericDocument with UTF8 encoding typedef GenericDocument > Document; -// defined here due to the dependency on GenericDocument -template -template -inline -GenericValue::GenericValue(const GenericValue& rhs, Allocator& allocator) -{ - switch (rhs.GetType()) { - case kObjectType: - case kArrayType: { // perform deep copy via SAX Handler - GenericDocument d(&allocator); - rhs.Accept(d); - RawAssign(*d.stack_.template Pop(1)); - } - break; - case kStringType: - if (rhs.flags_ == kConstStringFlag) { - flags_ = rhs.flags_; - data_ = *reinterpret_cast(&rhs.data_); - } else { - SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); - } - break; - default: // kNumberType, kTrueType, kFalseType, kNullType - flags_ = rhs.flags_; - data_ = *reinterpret_cast(&rhs.data_); - } -} +//! Helper class for accessing Value of array type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetArray(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericArray { +public: + typedef GenericArray ConstArray; + typedef GenericArray Array; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef ValueType* ValueIterator; // This may be const or non-const iterator + typedef const ValueT* ConstValueIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; -RAPIDJSON_NAMESPACE_END + template + friend class GenericValue; -#if defined(_MSC_VER) || defined(__GNUC__) -RAPIDJSON_DIAG_POP + GenericArray(const GenericArray& rhs) : value_(rhs.value_) {} + GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } + ~GenericArray() {} + + SizeType Size() const { return value_.Size(); } + SizeType Capacity() const { return value_.Capacity(); } + bool Empty() const { return value_.Empty(); } + void Clear() const { value_.Clear(); } + ValueType& operator[](SizeType index) const { return value_[index]; } + ValueIterator Begin() const { return value_.Begin(); } + ValueIterator End() const { return value_.End(); } + GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; } + GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + GenericArray PopBack() const { value_.PopBack(); return *this; } + ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); } + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + ValueIterator begin() const { return value_.Begin(); } + ValueIterator end() const { return value_.End(); } #endif +private: + GenericArray(); + GenericArray(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +//! Helper class for accessing Value of object type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetObject(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericObject { +public: + typedef GenericObject ConstObject; + typedef GenericObject Object; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef GenericMemberIterator MemberIterator; // This may be const or non-const iterator + typedef GenericMemberIterator ConstMemberIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename ValueType::Ch Ch; + + template + friend class GenericValue; + + GenericObject(const GenericObject& rhs) : value_(rhs.value_) {} + GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } + ~GenericObject() {} + + SizeType MemberCount() const { return value_.MemberCount(); } + bool ObjectEmpty() const { return value_.ObjectEmpty(); } + template ValueType& operator[](T* name) const { return value_[name]; } + template ValueType& operator[](const GenericValue& name) const { return value_[name]; } +#if RAPIDJSON_HAS_STDSTRING + ValueType& operator[](const std::basic_string& name) const { return value_[name]; } +#endif + MemberIterator MemberBegin() const { return value_.MemberBegin(); } + MemberIterator MemberEnd() const { return value_.MemberEnd(); } + bool HasMember(const Ch* name) const { return value_.HasMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } +#endif + template bool HasMember(const GenericValue& name) const { return value_.HasMember(name); } + MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); } + template MemberIterator FindMember(const GenericValue& name) const { return value_.FindMember(name); } +#if RAPIDJSON_HAS_STDSTRING + MemberIterator FindMember(const std::basic_string& name) const { return value_.FindMember(name); } +#endif + GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_STDSTRING + GenericObject AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + void RemoveAllMembers() { value_.RemoveAllMembers(); } + bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } +#endif + template bool RemoveMember(const GenericValue& name) const { return value_.RemoveMember(name); } + MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); } + MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); } + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); } + bool EraseMember(const Ch* name) const { return value_.EraseMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } +#endif + template bool EraseMember(const GenericValue& name) const { return value_.EraseMember(name); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + MemberIterator begin() const { return value_.MemberBegin(); } + MemberIterator end() const { return value_.MemberEnd(); } +#endif + +private: + GenericObject(); + GenericObject(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + #endif // RAPIDJSON_DOCUMENT_H_ diff --git a/contrib/rapidjson/include/rapidjson/encodedstream.h b/contrib/rapidjson/include/rapidjson/encodedstream.h index 7c8863fee..223601c05 100644 --- a/contrib/rapidjson/include/rapidjson/encodedstream.h +++ b/contrib/rapidjson/include/rapidjson/encodedstream.h @@ -15,13 +15,19 @@ #ifndef RAPIDJSON_ENCODEDSTREAM_H_ #define RAPIDJSON_ENCODEDSTREAM_H_ -#include "rapidjson.h" +#include "stream.h" +#include "memorystream.h" #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + RAPIDJSON_NAMESPACE_BEGIN //! Input byte stream wrapper with a statically bound encoding. @@ -57,10 +63,38 @@ private: Ch current_; }; +//! Specialized for UTF8 MemoryStream. +template <> +class EncodedInputStream, MemoryStream> { +public: + typedef UTF8<>::Ch Ch; + + EncodedInputStream(MemoryStream& is) : is_(is) { + if (static_cast(is_.Peek()) == 0xEFu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBBu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBFu) is_.Take(); + } + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) {} + void Flush() {} + Ch* PutBegin() { return 0; } + size_t PutEnd(Ch*) { return 0; } + + MemoryStream& is_; + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); +}; + //! Output byte stream wrapper with statically bound encoding. /*! \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. - \tparam InputByteStream Type of input byte stream. For example, FileWriteStream. + \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream. */ template class EncodedOutputStream { @@ -77,8 +111,8 @@ public: void Flush() { os_.Flush(); } // Not implemented - Ch Peek() const { RAPIDJSON_ASSERT(false); } - Ch Take() { RAPIDJSON_ASSERT(false); } + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } @@ -142,11 +176,11 @@ private: // FF FE UTF-16LE // EF BB BF UTF-8 - const unsigned char* c = (const unsigned char *)is_->Peek4(); + const unsigned char* c = reinterpret_cast(is_->Peek4()); if (!c) return; - unsigned bom = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24); + unsigned bom = static_cast(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)); hasBOM_ = false; if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } @@ -166,7 +200,7 @@ private: // xx xx xx xx UTF-8 if (!hasBOM_) { - unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + int pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); switch (pattern) { case 0x08: type_ = kUTF32BE; break; case 0x0A: type_ = kUTF16BE; break; @@ -193,7 +227,7 @@ private: //! Output stream wrapper with dynamically bound encoding and automatic encoding detection. /*! \tparam CharType Type of character for writing. - \tparam InputByteStream type of output byte stream to be wrapped. + \tparam OutputByteStream type of output byte stream to be wrapped. */ template class AutoUTFOutputStream { @@ -227,8 +261,8 @@ public: void Flush() { os_->Flush(); } // Not implemented - Ch Peek() const { RAPIDJSON_ASSERT(false); } - Ch Take() { RAPIDJSON_ASSERT(false); } + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } @@ -254,6 +288,10 @@ private: RAPIDJSON_NAMESPACE_END +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif diff --git a/contrib/rapidjson/include/rapidjson/encodings.h b/contrib/rapidjson/include/rapidjson/encodings.h index 90b46ed32..0df1c3435 100644 --- a/contrib/rapidjson/include/rapidjson/encodings.h +++ b/contrib/rapidjson/include/rapidjson/encodings.h @@ -120,19 +120,45 @@ struct UTF8 { } } + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + PutUnsafe(os, static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + PutUnsafe(os, static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + PutUnsafe(os, static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + } + template static bool Decode(InputStream& is, unsigned* codepoint) { -#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | ((unsigned char)c & 0x3Fu) -#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0) +#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) +#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) #define TAIL() COPY(); TRANS(0x70) - Ch c = is.Take(); + typename InputStream::Ch c = is.Take(); if (!(c & 0x80)) { - *codepoint = (unsigned char)c; + *codepoint = static_cast(c); return true; } - unsigned char type = GetRange((unsigned char)c); - *codepoint = (0xFF >> type) & (unsigned char)c; + unsigned char type = GetRange(static_cast(c)); + if (type >= 32) { + *codepoint = 0; + } else { + *codepoint = (0xFFu >> type) & static_cast(c); + } bool result = true; switch (type) { case 2: TAIL(); return result; @@ -152,7 +178,7 @@ struct UTF8 { template static bool Validate(InputStream& is, OutputStream& os) { #define COPY() os.Put(c = is.Take()) -#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0) +#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) #define TAIL() COPY(); TRANS(0x70) Ch c; COPY(); @@ -160,7 +186,7 @@ struct UTF8 { return true; bool result = true; - switch (GetRange((unsigned char)c)) { + switch (GetRange(static_cast(c))) { case 2: TAIL(); return result; case 3: TAIL(); TAIL(); return result; case 4: COPY(); TRANS(0x50); TAIL(); return result; @@ -196,12 +222,12 @@ struct UTF8 { template static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - Ch c = Take(is); - if ((unsigned char)c != 0xEFu) return c; + typename InputByteStream::Ch c = Take(is); + if (static_cast(c) != 0xEFu) return c; c = is.Take(); - if ((unsigned char)c != 0xBBu) return c; + if (static_cast(c) != 0xBBu) return c; c = is.Take(); - if ((unsigned char)c != 0xBFu) return c; + if (static_cast(c) != 0xBFu) return c; c = is.Take(); return c; } @@ -209,13 +235,15 @@ struct UTF8 { template static Ch Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - return is.Take(); + return static_cast(is.Take()); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(0xEFu); os.Put(0xBBu); os.Put(0xBFu); + os.Put(static_cast(0xEFu)); + os.Put(static_cast(0xBBu)); + os.Put(static_cast(0xBFu)); } template @@ -255,22 +283,38 @@ struct UTF16 { RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); unsigned v = codepoint - 0x10000; os.Put(static_cast((v >> 10) | 0xD800)); - os.Put((v & 0x3FF) | 0xDC00); + os.Put(static_cast((v & 0x3FF) | 0xDC00)); + } + } + + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + PutUnsafe(os, static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + PutUnsafe(os, static_cast((v >> 10) | 0xD800)); + PutUnsafe(os, static_cast((v & 0x3FF) | 0xDC00)); } } template static bool Decode(InputStream& is, unsigned* codepoint) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); - Ch c = is.Take(); + typename InputStream::Ch c = is.Take(); if (c < 0xD800 || c > 0xDFFF) { - *codepoint = c; + *codepoint = static_cast(c); return true; } else if (c <= 0xDBFF) { - *codepoint = (c & 0x3FF) << 10; + *codepoint = (static_cast(c) & 0x3FF) << 10; c = is.Take(); - *codepoint |= (c & 0x3FF); + *codepoint |= (static_cast(c) & 0x3FF); *codepoint += 0x10000; return c >= 0xDC00 && c <= 0xDFFF; } @@ -281,8 +325,8 @@ struct UTF16 { static bool Validate(InputStream& is, OutputStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); - Ch c; - os.Put(c = is.Take()); + typename InputStream::Ch c; + os.Put(static_cast(c = is.Take())); if (c < 0xD800 || c > 0xDFFF) return true; else if (c <= 0xDBFF) { @@ -300,28 +344,29 @@ struct UTF16LE : UTF16 { static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); - return (unsigned short)c == 0xFEFFu ? Take(is) : c; + return static_cast(c) == 0xFEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = (unsigned char)is.Take(); - c |= (unsigned char)is.Take() << 8; - return c; + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + return static_cast(c); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(0xFFu); os.Put(0xFEu); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(c & 0xFFu); - os.Put((c >> 8) & 0xFFu); + os.Put(static_cast(static_cast(c) & 0xFFu)); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); } }; @@ -332,28 +377,29 @@ struct UTF16BE : UTF16 { static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); - return (unsigned short)c == 0xFEFFu ? Take(is) : c; + return static_cast(c) == 0xFEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = (unsigned char)is.Take() << 8; - c |= (unsigned char)is.Take(); - return c; + unsigned c = static_cast(static_cast(is.Take())) << 8; + c |= static_cast(is.Take()); + return static_cast(c); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(0xFEu); os.Put(0xFFu); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put((c >> 8) & 0xFFu); - os.Put(c & 0xFFu); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + os.Put(static_cast(static_cast(c) & 0xFFu)); } }; @@ -382,6 +428,13 @@ struct UTF32 { os.Put(codepoint); } + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, codepoint); + } + template static bool Decode(InputStream& is, unsigned* codepoint) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); @@ -406,32 +459,35 @@ struct UTF32LE : UTF32 { static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); - return (unsigned)c == 0x0000FEFFu ? Take(is) : c; + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = (unsigned char)is.Take(); - c |= (unsigned char)is.Take() << 8; - c |= (unsigned char)is.Take() << 16; - c |= (unsigned char)is.Take() << 24; - return c; + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 24; + return static_cast(c); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(0xFFu); os.Put(0xFEu); os.Put(0x00u); os.Put(0x00u); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(c & 0xFFu); - os.Put((c >> 8) & 0xFFu); - os.Put((c >> 16) & 0xFFu); - os.Put((c >> 24) & 0xFFu); + os.Put(static_cast(c & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 24) & 0xFFu)); } }; @@ -442,32 +498,35 @@ struct UTF32BE : UTF32 { static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); - return (unsigned)c == 0x0000FEFFu ? Take(is) : c; + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; } template static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = (unsigned char)is.Take() << 24; - c |= (unsigned char)is.Take() << 16; - c |= (unsigned char)is.Take() << 8; - c |= (unsigned char)is.Take(); - return c; + unsigned c = static_cast(static_cast(is.Take())) << 24; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())); + return static_cast(c); } template static void PutBOM(OutputByteStream& os) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(0x00u); os.Put(0x00u); os.Put(0xFEu); os.Put(0xFFu); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); } template static void Put(OutputByteStream& os, CharType c) { RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put((c >> 24) & 0xFFu); - os.Put((c >> 16) & 0xFFu); - os.Put((c >> 8) & 0xFFu); - os.Put(c & 0xFFu); + os.Put(static_cast((c >> 24) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast(c & 0xFFu)); } }; @@ -491,31 +550,37 @@ struct ASCII { os.Put(static_cast(codepoint & 0xFF)); } + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + PutUnsafe(os, static_cast(codepoint & 0xFF)); + } + template static bool Decode(InputStream& is, unsigned* codepoint) { - unsigned char c = static_cast(is.Take()); + uint8_t c = static_cast(is.Take()); *codepoint = c; return c <= 0X7F; } template static bool Validate(InputStream& is, OutputStream& os) { - unsigned char c = is.Take(); - os.Put(c); + uint8_t c = static_cast(is.Take()); + os.Put(static_cast(c)); return c <= 0x7F; } template static CharType TakeBOM(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - Ch c = Take(is); - return c; + uint8_t c = static_cast(Take(is)); + return static_cast(c); } template static Ch Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - return is.Take(); + return static_cast(is.Take()); } template @@ -555,21 +620,28 @@ struct AutoUTF { #define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x template - RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) { + static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) { typedef void (*EncodeFunc)(OutputStream&, unsigned); static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; (*f[os.GetType()])(os, codepoint); } + template + static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; + (*f[os.GetType()])(os, codepoint); + } + template - RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { + static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) { typedef bool (*DecodeFunc)(InputStream&, unsigned*); static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; return (*f[is.GetType()])(is, codepoint); } template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { typedef bool (*ValidateFunc)(InputStream&, OutputStream&); static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; return (*f[is.GetType()])(is, os); @@ -586,7 +658,7 @@ template struct Transcoder { //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. template - RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { unsigned codepoint; if (!SourceEncoding::Decode(is, &codepoint)) return false; @@ -594,31 +666,50 @@ struct Transcoder { return true; } + template + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::EncodeUnsafe(os, codepoint); + return true; + } + //! Validate one Unicode codepoint from an encoded stream. template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { return Transcode(is, os); // Since source/target encoding is different, must transcode. } }; +// Forward declaration. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c); + //! Specialization of Transcoder with same source and target encoding. template struct Transcoder { template - RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. return true; } template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { return Encoding::Validate(is, os); // source/target encoding are the same } }; RAPIDJSON_NAMESPACE_END -#if defined(__GNUC__) || defined(_MSV_VER) +#if defined(__GNUC__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif diff --git a/contrib/rapidjson/include/rapidjson/error/en.h b/contrib/rapidjson/include/rapidjson/error/en.h index d5f9caab8..2db838bff 100644 --- a/contrib/rapidjson/include/rapidjson/error/en.h +++ b/contrib/rapidjson/include/rapidjson/error/en.h @@ -12,11 +12,17 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. -#ifndef RAPIDJSON_ERROR_EN_H__ -#define RAPIDJSON_ERROR_EN_H__ +#ifndef RAPIDJSON_ERROR_EN_H_ +#define RAPIDJSON_ERROR_EN_H_ #include "error.h" +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(covered-switch-default) +#endif + RAPIDJSON_NAMESPACE_BEGIN //! Maps error code of parsing into error message. @@ -32,7 +38,7 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); - case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not follow by other values."); + case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values."); case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); @@ -55,11 +61,14 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); - default: - return RAPIDJSON_ERROR_STRING("Unknown error."); + default: return RAPIDJSON_ERROR_STRING("Unknown error."); } } RAPIDJSON_NAMESPACE_END -#endif // RAPIDJSON_ERROR_EN_H__ +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_EN_H_ diff --git a/contrib/rapidjson/include/rapidjson/error/error.h b/contrib/rapidjson/include/rapidjson/error/error.h index f9094fb95..9311d2f03 100644 --- a/contrib/rapidjson/include/rapidjson/error/error.h +++ b/contrib/rapidjson/include/rapidjson/error/error.h @@ -12,11 +12,16 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. -#ifndef RAPIDJSON_ERROR_ERROR_H__ -#define RAPIDJSON_ERROR_ERROR_H__ +#ifndef RAPIDJSON_ERROR_ERROR_H_ +#define RAPIDJSON_ERROR_ERROR_H_ #include "../rapidjson.h" +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + /*! \file error.h */ /*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ @@ -99,7 +104,9 @@ enum ParseErrorCode { \see GenericReader::Parse, GenericDocument::Parse */ struct ParseResult { - + //!! Unspecified boolean type + typedef bool (ParseResult::*BooleanType)() const; +public: //! Default constructor, no error. ParseResult() : code_(kParseErrorNone), offset_(0) {} //! Constructor to set an error. @@ -110,8 +117,8 @@ struct ParseResult { //! Get the error offset, if \ref IsError(), 0 otherwise. size_t Offset() const { return offset_; } - //! Conversion to \c bool, returns \c true, iff !\ref IsError(). - operator bool() const { return !IsError(); } + //! Explicit conversion to \c bool, returns \c true, iff !\ref IsError(). + operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; } //! Whether the result is an error. bool IsError() const { return code_ != kParseErrorNone; } @@ -119,6 +126,10 @@ struct ParseResult { bool operator==(ParseErrorCode code) const { return code_ == code; } friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + bool operator!=(const ParseResult& that) const { return !(*this == that); } + bool operator!=(ParseErrorCode code) const { return !(*this == code); } + friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return err != code; } + //! Reset error code. void Clear() { Set(kParseErrorNone); } //! Update error code and offset. @@ -143,4 +154,8 @@ typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); RAPIDJSON_NAMESPACE_END -#endif // RAPIDJSON_ERROR_ERROR_H__ +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_ERROR_H_ diff --git a/contrib/rapidjson/include/rapidjson/filereadstream.h b/contrib/rapidjson/include/rapidjson/filereadstream.h index 3913eb74b..b56ea13b3 100644 --- a/contrib/rapidjson/include/rapidjson/filereadstream.h +++ b/contrib/rapidjson/include/rapidjson/filereadstream.h @@ -15,9 +15,16 @@ #ifndef RAPIDJSON_FILEREADSTREAM_H_ #define RAPIDJSON_FILEREADSTREAM_H_ -#include "rapidjson.h" +#include "stream.h" #include +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + RAPIDJSON_NAMESPACE_BEGIN //! File byte stream for input using fread(). @@ -85,4 +92,8 @@ private: RAPIDJSON_NAMESPACE_END +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + #endif // RAPIDJSON_FILESTREAM_H_ diff --git a/contrib/rapidjson/include/rapidjson/filewritestream.h b/contrib/rapidjson/include/rapidjson/filewritestream.h index dfb9cbd02..6378dd60e 100644 --- a/contrib/rapidjson/include/rapidjson/filewritestream.h +++ b/contrib/rapidjson/include/rapidjson/filewritestream.h @@ -15,9 +15,14 @@ #ifndef RAPIDJSON_FILEWRITESTREAM_H_ #define RAPIDJSON_FILEWRITESTREAM_H_ -#include "rapidjson.h" +#include "stream.h" #include +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +#endif + RAPIDJSON_NAMESPACE_BEGIN //! Wrapper of C file stream for input using fread(). @@ -57,7 +62,11 @@ public: void Flush() { if (current_ != buffer_) { - fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + size_t result = fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + if (result < static_cast(current_ - buffer_)) { + // failure deliberately ignored at this time + // added to avoid warn_unused_result build errors + } current_ = buffer_; } } @@ -88,4 +97,8 @@ inline void PutN(FileWriteStream& stream, char c, size_t n) { RAPIDJSON_NAMESPACE_END +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + #endif // RAPIDJSON_FILESTREAM_H_ diff --git a/contrib/rapidjson/include/rapidjson/fwd.h b/contrib/rapidjson/include/rapidjson/fwd.h new file mode 100644 index 000000000..e8104e841 --- /dev/null +++ b/contrib/rapidjson/include/rapidjson/fwd.h @@ -0,0 +1,151 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FWD_H_ +#define RAPIDJSON_FWD_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +// encodings.h + +template struct UTF8; +template struct UTF16; +template struct UTF16BE; +template struct UTF16LE; +template struct UTF32; +template struct UTF32BE; +template struct UTF32LE; +template struct ASCII; +template struct AutoUTF; + +template +struct Transcoder; + +// allocators.h + +class CrtAllocator; + +template +class MemoryPoolAllocator; + +// stream.h + +template +struct GenericStringStream; + +typedef GenericStringStream > StringStream; + +template +struct GenericInsituStringStream; + +typedef GenericInsituStringStream > InsituStringStream; + +// stringbuffer.h + +template +class GenericStringBuffer; + +typedef GenericStringBuffer, CrtAllocator> StringBuffer; + +// filereadstream.h + +class FileReadStream; + +// filewritestream.h + +class FileWriteStream; + +// memorybuffer.h + +template +struct GenericMemoryBuffer; + +typedef GenericMemoryBuffer MemoryBuffer; + +// memorystream.h + +struct MemoryStream; + +// reader.h + +template +struct BaseReaderHandler; + +template +class GenericReader; + +typedef GenericReader, UTF8, CrtAllocator> Reader; + +// writer.h + +template +class Writer; + +// prettywriter.h + +template +class PrettyWriter; + +// document.h + +template +struct GenericMember; + +template +class GenericMemberIterator; + +template +struct GenericStringRef; + +template +class GenericValue; + +typedef GenericValue, MemoryPoolAllocator > Value; + +template +class GenericDocument; + +typedef GenericDocument, MemoryPoolAllocator, CrtAllocator> Document; + +// pointer.h + +template +class GenericPointer; + +typedef GenericPointer Pointer; + +// schema.h + +template +class IGenericRemoteSchemaDocumentProvider; + +template +class GenericSchemaDocument; + +typedef GenericSchemaDocument SchemaDocument; +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +template < + typename SchemaDocumentType, + typename OutputHandler, + typename StateAllocator> +class GenericSchemaValidator; + +typedef GenericSchemaValidator, void>, CrtAllocator> SchemaValidator; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSONFWD_H_ diff --git a/contrib/rapidjson/include/rapidjson/internal/biginteger.h b/contrib/rapidjson/include/rapidjson/internal/biginteger.h index 99a30acf6..9d3e88c99 100644 --- a/contrib/rapidjson/include/rapidjson/internal/biginteger.h +++ b/contrib/rapidjson/include/rapidjson/internal/biginteger.h @@ -19,6 +19,7 @@ #if defined(_MSC_VER) && defined(_M_AMD64) #include // for _umul128 +#pragma intrinsic(_umul128) #endif RAPIDJSON_NAMESPACE_BEGIN @@ -50,7 +51,16 @@ public: if (length > 0) AppendDecimal64(decimals + i, decimals + i + length); } - + + BigInteger& operator=(const BigInteger &rhs) + { + if (this != &rhs) { + count_ = rhs.count_; + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + return *this; + } + BigInteger& operator=(uint64_t u) { digits_[0] = u; count_ = 1; @@ -230,7 +240,7 @@ private: uint64_t r = 0; for (const char* p = begin; p != end; ++p) { RAPIDJSON_ASSERT(*p >= '0' && *p <= '9'); - r = r * 10 + (*p - '0'); + r = r * 10u + static_cast(*p - '0'); } return r; } diff --git a/contrib/rapidjson/include/rapidjson/internal/diyfp.h b/contrib/rapidjson/include/rapidjson/internal/diyfp.h index 3b6c4238c..29abf8046 100644 --- a/contrib/rapidjson/include/rapidjson/internal/diyfp.h +++ b/contrib/rapidjson/include/rapidjson/internal/diyfp.h @@ -21,9 +21,10 @@ #include "../rapidjson.h" -#if defined(_MSC_VER) && defined(_M_AMD64) +#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER) #include #pragma intrinsic(_BitScanReverse64) +#pragma intrinsic(_umul128) #endif RAPIDJSON_NAMESPACE_BEGIN @@ -34,8 +35,13 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + struct DiyFp { - DiyFp() {} + DiyFp() : f(), e() {} DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} @@ -232,8 +238,8 @@ inline DiyFp GetCachedPower(int e, int* K) { } inline DiyFp GetCachedPower10(int exp, int *outExp) { - unsigned index = (exp + 348) / 8; - *outExp = -348 + index * 8; + unsigned index = (static_cast(exp) + 348u) / 8u; + *outExp = -348 + static_cast(index) * 8; return GetCachedPowerByIndex(index); } @@ -241,6 +247,11 @@ inline DiyFp GetCachedPower10(int exp, int *outExp) { RAPIDJSON_DIAG_POP #endif +#ifdef __clang__ +RAPIDJSON_DIAG_POP +RAPIDJSON_DIAG_OFF(padded) +#endif + } // namespace internal RAPIDJSON_NAMESPACE_END diff --git a/contrib/rapidjson/include/rapidjson/internal/dtoa.h b/contrib/rapidjson/include/rapidjson/internal/dtoa.h index 2d8d2e46a..bf2e9b2e5 100644 --- a/contrib/rapidjson/include/rapidjson/internal/dtoa.h +++ b/contrib/rapidjson/include/rapidjson/internal/dtoa.h @@ -29,6 +29,7 @@ namespace internal { #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124 #endif inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { @@ -40,7 +41,7 @@ inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uin } } -inline unsigned CountDecimalDigit32(uint32_t n) { +inline int CountDecimalDigit32(uint32_t n) { // Simple pure C++ implementation was faster than __builtin_clz version in this situation. if (n < 10) return 1; if (n < 100) return 2; @@ -101,7 +102,8 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff kappa--; if (p2 < delta) { *K += kappa; - GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-kappa]); + int index = -kappa; + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[index] : 0)); return; } } @@ -145,10 +147,10 @@ inline char* WriteExponent(int K, char* buffer) { return buffer; } -inline char* Prettify(char* buffer, int length, int k) { +inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { const int kk = length + k; // 10^(kk-1) <= v < 10^kk - if (length <= kk && kk <= 21) { + if (0 <= k && kk <= 21) { // 1234e7 -> 12340000000 for (int i = length; i < kk; i++) buffer[i] = '0'; @@ -158,19 +160,44 @@ inline char* Prettify(char* buffer, int length, int k) { } else if (0 < kk && kk <= 21) { // 1234e-2 -> 12.34 - std::memmove(&buffer[kk + 1], &buffer[kk], length - kk); + std::memmove(&buffer[kk + 1], &buffer[kk], static_cast(length - kk)); buffer[kk] = '.'; - return &buffer[length + 1]; + if (0 > k + maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = kk + maxDecimalPlaces; i > kk + 1; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[kk + 2]; // Reserve one zero + } + else + return &buffer[length + 1]; } else if (-6 < kk && kk <= 0) { // 1234e-6 -> 0.001234 const int offset = 2 - kk; - std::memmove(&buffer[offset], &buffer[0], length); + std::memmove(&buffer[offset], &buffer[0], static_cast(length)); buffer[0] = '0'; buffer[1] = '.'; for (int i = 2; i < offset; i++) buffer[i] = '0'; - return &buffer[length + offset]; + if (length - kk > maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = maxDecimalPlaces + 1; i > 2; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[3]; // Reserve one zero + } + else + return &buffer[length + offset]; + } + else if (kk < -maxDecimalPlaces) { + // Truncate to zero + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; } else if (length == 1) { // 1e30 @@ -179,14 +206,15 @@ inline char* Prettify(char* buffer, int length, int k) { } else { // 1234e30 -> 1.234e33 - std::memmove(&buffer[2], &buffer[1], length - 1); + std::memmove(&buffer[2], &buffer[1], static_cast(length - 1)); buffer[1] = '.'; buffer[length + 1] = 'e'; return WriteExponent(kk - 1, &buffer[0 + length + 2]); } } -inline char* dtoa(double value, char* buffer) { +inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) { + RAPIDJSON_ASSERT(maxDecimalPlaces >= 1); Double d(value); if (d.IsZero()) { if (d.Sign()) @@ -203,7 +231,7 @@ inline char* dtoa(double value, char* buffer) { } int length, K; Grisu2(value, buffer, &length, &K); - return Prettify(buffer, length, K); + return Prettify(buffer, length, K, maxDecimalPlaces); } } diff --git a/contrib/rapidjson/include/rapidjson/internal/ieee754.h b/contrib/rapidjson/include/rapidjson/internal/ieee754.h index e3f03364c..c2684ba2a 100644 --- a/contrib/rapidjson/include/rapidjson/internal/ieee754.h +++ b/contrib/rapidjson/include/rapidjson/internal/ieee754.h @@ -40,6 +40,7 @@ public: bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; } bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; } + bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; } bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; } bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; } @@ -47,7 +48,7 @@ public: int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } - static unsigned EffectiveSignificandSize(int order) { + static int EffectiveSignificandSize(int order) { if (order >= -1021) return 53; else if (order <= -1074) diff --git a/contrib/rapidjson/include/rapidjson/internal/regex.h b/contrib/rapidjson/include/rapidjson/internal/regex.h new file mode 100644 index 000000000..e1a2faae5 --- /dev/null +++ b/contrib/rapidjson/include/rapidjson/internal/regex.h @@ -0,0 +1,734 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_REGEX_H_ +#define RAPIDJSON_INTERNAL_REGEX_H_ + +#include "../allocators.h" +#include "../stream.h" +#include "stack.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(implicit-fallthrough) +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#if __GNUC__ >= 7 +RAPIDJSON_DIAG_OFF(implicit-fallthrough) +#endif +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +#ifndef RAPIDJSON_REGEX_VERBOSE +#define RAPIDJSON_REGEX_VERBOSE 0 +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// DecodedStream + +template +class DecodedStream { +public: + DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } + unsigned Peek() { return codepoint_; } + unsigned Take() { + unsigned c = codepoint_; + if (c) // No further decoding when '\0' + Decode(); + return c; + } + +private: + void Decode() { + if (!Encoding::Decode(ss_, &codepoint_)) + codepoint_ = 0; + } + + SourceStream& ss_; + unsigned codepoint_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericRegex + +static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 +static const SizeType kRegexInvalidRange = ~SizeType(0); + +template +class GenericRegexSearch; + +//! Regular expression engine with subset of ECMAscript grammar. +/*! + Supported regular expression syntax: + - \c ab Concatenation + - \c a|b Alternation + - \c a? Zero or one + - \c a* Zero or more + - \c a+ One or more + - \c a{3} Exactly 3 times + - \c a{3,} At least 3 times + - \c a{3,5} 3 to 5 times + - \c (ab) Grouping + - \c ^a At the beginning + - \c a$ At the end + - \c . Any character + - \c [abc] Character classes + - \c [a-c] Character class range + - \c [a-z0-9_] Character class combination + - \c [^abc] Negated character classes + - \c [^a-c] Negated character class range + - \c [\b] Backspace (U+0008) + - \c \\| \\\\ ... Escape characters + - \c \\f Form feed (U+000C) + - \c \\n Line feed (U+000A) + - \c \\r Carriage return (U+000D) + - \c \\t Tab (U+0009) + - \c \\v Vertical tab (U+000B) + + \note This is a Thompson NFA engine, implemented with reference to + Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).", + https://swtch.com/~rsc/regexp/regexp1.html +*/ +template +class GenericRegex { +public: + typedef Encoding EncodingType; + typedef typename Encoding::Ch Ch; + template friend class GenericRegexSearch; + + GenericRegex(const Ch* source, Allocator* allocator = 0) : + states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), + anchorBegin_(), anchorEnd_() + { + GenericStringStream ss(source); + DecodedStream, Encoding> ds(ss); + Parse(ds); + } + + ~GenericRegex() {} + + bool IsValid() const { + return root_ != kRegexInvalidState; + } + +private: + enum Operator { + kZeroOrOne, + kZeroOrMore, + kOneOrMore, + kConcatenation, + kAlternation, + kLeftParenthesis + }; + + static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.' + static const unsigned kRangeCharacterClass = 0xFFFFFFFE; + static const unsigned kRangeNegationFlag = 0x80000000; + + struct Range { + unsigned start; // + unsigned end; + SizeType next; + }; + + struct State { + SizeType out; //!< Equals to kInvalid for matching state + SizeType out1; //!< Equals to non-kInvalid for split + SizeType rangeStart; + unsigned codepoint; + }; + + struct Frag { + Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {} + SizeType start; + SizeType out; //!< link-list of all output states + SizeType minIndex; + }; + + State& GetState(SizeType index) { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + const State& GetState(SizeType index) const { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + Range& GetRange(SizeType index) { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + const Range& GetRange(SizeType index) const { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + template + void Parse(DecodedStream& ds) { + Allocator allocator; + Stack operandStack(&allocator, 256); // Frag + Stack operatorStack(&allocator, 256); // Operator + Stack atomCountStack(&allocator, 256); // unsigned (Atom per parenthesis) + + *atomCountStack.template Push() = 0; + + unsigned codepoint; + while (ds.Peek() != 0) { + switch (codepoint = ds.Take()) { + case '^': + anchorBegin_ = true; + break; + + case '$': + anchorEnd_ = true; + break; + + case '|': + while (!operatorStack.Empty() && *operatorStack.template Top() < kAlternation) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + *operatorStack.template Push() = kAlternation; + *atomCountStack.template Top() = 0; + break; + + case '(': + *operatorStack.template Push() = kLeftParenthesis; + *atomCountStack.template Push() = 0; + break; + + case ')': + while (!operatorStack.Empty() && *operatorStack.template Top() != kLeftParenthesis) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + if (operatorStack.Empty()) + return; + operatorStack.template Pop(1); + atomCountStack.template Pop(1); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '?': + if (!Eval(operandStack, kZeroOrOne)) + return; + break; + + case '*': + if (!Eval(operandStack, kZeroOrMore)) + return; + break; + + case '+': + if (!Eval(operandStack, kOneOrMore)) + return; + break; + + case '{': + { + unsigned n, m; + if (!ParseUnsigned(ds, &n)) + return; + + if (ds.Peek() == ',') { + ds.Take(); + if (ds.Peek() == '}') + m = kInfinityQuantifier; + else if (!ParseUnsigned(ds, &m) || m < n) + return; + } + else + m = n; + + if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}') + return; + ds.Take(); + } + break; + + case '.': + PushOperand(operandStack, kAnyCharacterClass); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '[': + { + SizeType range; + if (!ParseRange(ds, &range)) + return; + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass); + GetState(s).rangeStart = range; + *operandStack.template Push() = Frag(s, s, s); + } + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '\\': // Escape character + if (!CharacterEscape(ds, &codepoint)) + return; // Unsupported escape character + // fall through to default + + default: // Pattern character + PushOperand(operandStack, codepoint); + ImplicitConcatenation(atomCountStack, operatorStack); + } + } + + while (!operatorStack.Empty()) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + + // Link the operand to matching state. + if (operandStack.GetSize() == sizeof(Frag)) { + Frag* e = operandStack.template Pop(1); + Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0)); + root_ = e->start; + +#if RAPIDJSON_REGEX_VERBOSE + printf("root: %d\n", root_); + for (SizeType i = 0; i < stateCount_ ; i++) { + State& s = GetState(i); + printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint); + } + printf("\n"); +#endif + } + } + + SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { + State* s = states_.template Push(); + s->out = out; + s->out1 = out1; + s->codepoint = codepoint; + s->rangeStart = kRegexInvalidRange; + return stateCount_++; + } + + void PushOperand(Stack& operandStack, unsigned codepoint) { + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint); + *operandStack.template Push() = Frag(s, s, s); + } + + void ImplicitConcatenation(Stack& atomCountStack, Stack& operatorStack) { + if (*atomCountStack.template Top()) + *operatorStack.template Push() = kConcatenation; + (*atomCountStack.template Top())++; + } + + SizeType Append(SizeType l1, SizeType l2) { + SizeType old = l1; + while (GetState(l1).out != kRegexInvalidState) + l1 = GetState(l1).out; + GetState(l1).out = l2; + return old; + } + + void Patch(SizeType l, SizeType s) { + for (SizeType next; l != kRegexInvalidState; l = next) { + next = GetState(l).out; + GetState(l).out = s; + } + } + + bool Eval(Stack& operandStack, Operator op) { + switch (op) { + case kConcatenation: + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2); + { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + Patch(e1.out, e2.start); + *operandStack.template Push() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex)); + } + return true; + + case kAlternation: + if (operandStack.GetSize() >= sizeof(Frag) * 2) { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + SizeType s = NewState(e1.start, e2.start, 0); + *operandStack.template Push() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex)); + return true; + } + return false; + + case kZeroOrOne: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + *operandStack.template Push() = Frag(s, Append(e.out, s), e.minIndex); + return true; + } + return false; + + case kZeroOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(s, s, e.minIndex); + return true; + } + return false; + + default: + RAPIDJSON_ASSERT(op == kOneOrMore); + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(e.start, s, e.minIndex); + return true; + } + return false; + } + } + + bool EvalQuantifier(Stack& operandStack, unsigned n, unsigned m) { + RAPIDJSON_ASSERT(n <= m); + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag)); + + if (n == 0) { + if (m == 0) // a{0} not support + return false; + else if (m == kInfinityQuantifier) + Eval(operandStack, kZeroOrMore); // a{0,} -> a* + else { + Eval(operandStack, kZeroOrOne); // a{0,5} -> a? + for (unsigned i = 0; i < m - 1; i++) + CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a? + for (unsigned i = 0; i < m - 1; i++) + Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a? + } + return true; + } + + for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a + CloneTopOperand(operandStack); + + if (m == kInfinityQuantifier) + Eval(operandStack, kOneOrMore); // a{3,} -> a a a+ + else if (m > n) { + CloneTopOperand(operandStack); // a{3,5} -> a a a a + Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a? + for (unsigned i = n; i < m - 1; i++) + CloneTopOperand(operandStack); // a{3,5} -> a a a a? a? + for (unsigned i = n; i < m; i++) + Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a? + } + + for (unsigned i = 0; i < n - 1; i++) + Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a? + + return true; + } + + static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; } + + void CloneTopOperand(Stack& operandStack) { + const Frag src = *operandStack.template Top(); // Copy constructor to prevent invalidation + SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_) + State* s = states_.template Push(count); + memcpy(s, &GetState(src.minIndex), count * sizeof(State)); + for (SizeType j = 0; j < count; j++) { + if (s[j].out != kRegexInvalidState) + s[j].out += count; + if (s[j].out1 != kRegexInvalidState) + s[j].out1 += count; + } + *operandStack.template Push() = Frag(src.start + count, src.out + count, src.minIndex + count); + stateCount_ += count; + } + + template + bool ParseUnsigned(DecodedStream& ds, unsigned* u) { + unsigned r = 0; + if (ds.Peek() < '0' || ds.Peek() > '9') + return false; + while (ds.Peek() >= '0' && ds.Peek() <= '9') { + if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295 + return false; // overflow + r = r * 10 + (ds.Take() - '0'); + } + *u = r; + return true; + } + + template + bool ParseRange(DecodedStream& ds, SizeType* range) { + bool isBegin = true; + bool negate = false; + int step = 0; + SizeType start = kRegexInvalidRange; + SizeType current = kRegexInvalidRange; + unsigned codepoint; + while ((codepoint = ds.Take()) != 0) { + if (isBegin) { + isBegin = false; + if (codepoint == '^') { + negate = true; + continue; + } + } + + switch (codepoint) { + case ']': + if (start == kRegexInvalidRange) + return false; // Error: nothing inside [] + if (step == 2) { // Add trailing '-' + SizeType r = NewRange('-'); + RAPIDJSON_ASSERT(current != kRegexInvalidRange); + GetRange(current).next = r; + } + if (negate) + GetRange(start).start |= kRangeNegationFlag; + *range = start; + return true; + + case '\\': + if (ds.Peek() == 'b') { + ds.Take(); + codepoint = 0x0008; // Escape backspace character + } + else if (!CharacterEscape(ds, &codepoint)) + return false; + // fall through to default + + default: + switch (step) { + case 1: + if (codepoint == '-') { + step++; + break; + } + // fall through to step 0 for other characters + + case 0: + { + SizeType r = NewRange(codepoint); + if (current != kRegexInvalidRange) + GetRange(current).next = r; + if (start == kRegexInvalidRange) + start = r; + current = r; + } + step = 1; + break; + + default: + RAPIDJSON_ASSERT(step == 2); + GetRange(current).end = codepoint; + step = 0; + } + } + } + return false; + } + + SizeType NewRange(unsigned codepoint) { + Range* r = ranges_.template Push(); + r->start = r->end = codepoint; + r->next = kRegexInvalidRange; + return rangeCount_++; + } + + template + bool CharacterEscape(DecodedStream& ds, unsigned* escapedCodepoint) { + unsigned codepoint; + switch (codepoint = ds.Take()) { + case '^': + case '$': + case '|': + case '(': + case ')': + case '?': + case '*': + case '+': + case '.': + case '[': + case ']': + case '{': + case '}': + case '\\': + *escapedCodepoint = codepoint; return true; + case 'f': *escapedCodepoint = 0x000C; return true; + case 'n': *escapedCodepoint = 0x000A; return true; + case 'r': *escapedCodepoint = 0x000D; return true; + case 't': *escapedCodepoint = 0x0009; return true; + case 'v': *escapedCodepoint = 0x000B; return true; + default: + return false; // Unsupported escape character + } + } + + Stack states_; + Stack ranges_; + SizeType root_; + SizeType stateCount_; + SizeType rangeCount_; + + static const unsigned kInfinityQuantifier = ~0u; + + // For SearchWithAnchoring() + bool anchorBegin_; + bool anchorEnd_; +}; + +template +class GenericRegexSearch { +public: + typedef typename RegexType::EncodingType Encoding; + typedef typename Encoding::Ch Ch; + + GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) : + regex_(regex), allocator_(allocator), ownAllocator_(0), + state0_(allocator, 0), state1_(allocator, 0), stateSet_() + { + RAPIDJSON_ASSERT(regex_.IsValid()); + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + stateSet_ = static_cast(allocator_->Malloc(GetStateSetSize())); + state0_.template Reserve(regex_.stateCount_); + state1_.template Reserve(regex_.stateCount_); + } + + ~GenericRegexSearch() { + Allocator::Free(stateSet_); + RAPIDJSON_DELETE(ownAllocator_); + } + + template + bool Match(InputStream& is) { + return SearchWithAnchoring(is, true, true); + } + + bool Match(const Ch* s) { + GenericStringStream is(s); + return Match(is); + } + + template + bool Search(InputStream& is) { + return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_); + } + + bool Search(const Ch* s) { + GenericStringStream is(s); + return Search(is); + } + +private: + typedef typename RegexType::State State; + typedef typename RegexType::Range Range; + + template + bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) { + DecodedStream ds(is); + + state0_.Clear(); + Stack *current = &state0_, *next = &state1_; + const size_t stateSetSize = GetStateSetSize(); + std::memset(stateSet_, 0, stateSetSize); + + bool matched = AddState(*current, regex_.root_); + unsigned codepoint; + while (!current->Empty() && (codepoint = ds.Take()) != 0) { + std::memset(stateSet_, 0, stateSetSize); + next->Clear(); + matched = false; + for (const SizeType* s = current->template Bottom(); s != current->template End(); ++s) { + const State& sr = regex_.GetState(*s); + if (sr.codepoint == codepoint || + sr.codepoint == RegexType::kAnyCharacterClass || + (sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) + { + matched = AddState(*next, sr.out) || matched; + if (!anchorEnd && matched) + return true; + } + if (!anchorBegin) + AddState(*next, regex_.root_); + } + internal::Swap(current, next); + } + + return matched; + } + + size_t GetStateSetSize() const { + return (regex_.stateCount_ + 31) / 32 * 4; + } + + // Return whether the added states is a match state + bool AddState(Stack& l, SizeType index) { + RAPIDJSON_ASSERT(index != kRegexInvalidState); + + const State& s = regex_.GetState(index); + if (s.out1 != kRegexInvalidState) { // Split + bool matched = AddState(l, s.out); + return AddState(l, s.out1) || matched; + } + else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) { + stateSet_[index >> 5] |= (1u << (index & 31)); + *l.template PushUnsafe() = index; + } + return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. + } + + bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { + bool yes = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0; + while (rangeIndex != kRegexInvalidRange) { + const Range& r = regex_.GetRange(rangeIndex); + if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end) + return yes; + rangeIndex = r.next; + } + return !yes; + } + + const RegexType& regex_; + Allocator* allocator_; + Allocator* ownAllocator_; + Stack state0_; + Stack state1_; + uint32_t* stateSet_; +}; + +typedef GenericRegex > Regex; +typedef GenericRegexSearch RegexSearch; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_REGEX_H_ diff --git a/contrib/rapidjson/include/rapidjson/internal/stack.h b/contrib/rapidjson/include/rapidjson/internal/stack.h index 722d56923..5c5398c35 100644 --- a/contrib/rapidjson/include/rapidjson/internal/stack.h +++ b/contrib/rapidjson/include/rapidjson/internal/stack.h @@ -15,7 +15,13 @@ #ifndef RAPIDJSON_INTERNAL_STACK_H_ #define RAPIDJSON_INTERNAL_STACK_H_ -#include "../rapidjson.h" +#include "../allocators.h" +#include "swap.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif RAPIDJSON_NAMESPACE_BEGIN namespace internal { @@ -32,7 +38,6 @@ public: // Optimization note: Do not allocate memory for stack_ in constructor. // Do it lazily when first Push() -> Expand() -> Resize(). Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { - RAPIDJSON_ASSERT(stackCapacity > 0); } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS @@ -81,6 +86,15 @@ public: } #endif + void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(stack_, rhs.stack_); + internal::Swap(stackTop_, rhs.stackTop_); + internal::Swap(stackEnd_, rhs.stackEnd_); + internal::Swap(initialCapacity_, rhs.initialCapacity_); + } + void Clear() { stackTop_ = stack_; } void ShrinkToFit() { @@ -98,11 +112,22 @@ public: // Optimization note: try to minimize the size of this function for force inline. // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. template - RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { + RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { // Expand the stack if needed - if (stackTop_ + sizeof(T) * count >= stackEnd_) + if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_)) Expand(count); + } + template + RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { + Reserve(count); + return PushUnsafe(count); + } + + template + RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { + RAPIDJSON_ASSERT(stackTop_); + RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_); T* ret = reinterpret_cast(stackTop_); stackTop_ += sizeof(T) * count; return ret; @@ -122,9 +147,32 @@ public: } template - T* Bottom() { return (T*)stack_; } + const T* Top() const { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + T* End() { return reinterpret_cast(stackTop_); } + + template + const T* End() const { return reinterpret_cast(stackTop_); } + + template + T* Bottom() { return reinterpret_cast(stack_); } + + template + const T* Bottom() const { return reinterpret_cast(stack_); } + + bool HasAllocator() const { + return allocator_ != 0; + } + + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } - Allocator& GetAllocator() { return *allocator_; } bool Empty() const { return stackTop_ == stack_; } size_t GetSize() const { return static_cast(stackTop_ - stack_); } size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } @@ -136,7 +184,7 @@ private: size_t newCapacity; if (stack_ == 0) { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); newCapacity = initialCapacity_; } else { newCapacity = GetCapacity(); @@ -151,7 +199,7 @@ private: void Resize(size_t newCapacity) { const size_t size = GetSize(); // Backup the current size - stack_ = (char*)allocator_->Realloc(stack_, GetCapacity(), newCapacity); + stack_ = static_cast(allocator_->Realloc(stack_, GetCapacity(), newCapacity)); stackTop_ = stack_ + size; stackEnd_ = stack_ + newCapacity; } @@ -176,4 +224,8 @@ private: } // namespace internal RAPIDJSON_NAMESPACE_END +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + #endif // RAPIDJSON_STACK_H_ diff --git a/contrib/rapidjson/include/rapidjson/internal/strfunc.h b/contrib/rapidjson/include/rapidjson/internal/strfunc.h index 84405065a..226439a76 100644 --- a/contrib/rapidjson/include/rapidjson/internal/strfunc.h +++ b/contrib/rapidjson/include/rapidjson/internal/strfunc.h @@ -15,7 +15,8 @@ #ifndef RAPIDJSON_INTERNAL_STRFUNC_H_ #define RAPIDJSON_INTERNAL_STRFUNC_H_ -#include "../rapidjson.h" +#include "../stream.h" +#include RAPIDJSON_NAMESPACE_BEGIN namespace internal { @@ -28,11 +29,40 @@ namespace internal { */ template inline SizeType StrLen(const Ch* s) { + RAPIDJSON_ASSERT(s != 0); const Ch* p = s; while (*p) ++p; return SizeType(p - s); } +template <> +inline SizeType StrLen(const char* s) { + return SizeType(std::strlen(s)); +} + +template <> +inline SizeType StrLen(const wchar_t* s) { + return SizeType(std::wcslen(s)); +} + +//! Returns number of code points in a encoded string. +template +bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { + RAPIDJSON_ASSERT(s != 0); + RAPIDJSON_ASSERT(outCount != 0); + GenericStringStream is(s); + const typename Encoding::Ch* end = s + length; + SizeType count = 0; + while (is.src_ < end) { + unsigned codepoint; + if (!Encoding::Decode(is, &codepoint)) + return false; + count++; + } + *outCount = count; + return true; +} + } // namespace internal RAPIDJSON_NAMESPACE_END diff --git a/contrib/rapidjson/include/rapidjson/internal/strtod.h b/contrib/rapidjson/include/rapidjson/internal/strtod.h index ace65f677..adf49e349 100644 --- a/contrib/rapidjson/include/rapidjson/internal/strtod.h +++ b/contrib/rapidjson/include/rapidjson/internal/strtod.h @@ -15,7 +15,6 @@ #ifndef RAPIDJSON_STRTOD_ #define RAPIDJSON_STRTOD_ -#include "../rapidjson.h" #include "ieee754.h" #include "biginteger.h" #include "diyfp.h" @@ -95,13 +94,13 @@ inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { hS_Exp2 -= common_Exp2; BigInteger dS = d; - dS.MultiplyPow5(dS_Exp5) <<= dS_Exp2; + dS.MultiplyPow5(static_cast(dS_Exp5)) <<= static_cast(dS_Exp2); BigInteger bS(bInt); - bS.MultiplyPow5(bS_Exp5) <<= bS_Exp2; + bS.MultiplyPow5(static_cast(bS_Exp5)) <<= static_cast(bS_Exp2); BigInteger hS(1); - hS.MultiplyPow5(hS_Exp5) <<= hS_Exp2; + hS.MultiplyPow5(static_cast(hS_Exp5)) <<= static_cast(hS_Exp2); BigInteger delta(0); dS.Difference(bS, &delta); @@ -134,22 +133,22 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) break; - significand = significand * 10 + (decimals[i] - '0'); + significand = significand * 10u + static_cast(decimals[i] - '0'); } if (i < length && decimals[i] >= '5') // Rounding significand++; size_t remaining = length - i; - const unsigned kUlpShift = 3; - const unsigned kUlp = 1 << kUlpShift; - int error = (remaining == 0) ? 0 : kUlp / 2; + const int kUlpShift = 3; + const int kUlp = 1 << kUlpShift; + int64_t error = (remaining == 0) ? 0 : kUlp / 2; DiyFp v(significand, 0); v = v.Normalize(); error <<= -v.e; - const int dExp = (int)decimalPosition - (int)i + exp; + const int dExp = static_cast(decimalPosition) - static_cast(i) + exp; int actualExp; DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); @@ -163,10 +162,10 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6 DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7 }; - int adjustment = dExp - actualExp - 1; + int adjustment = dExp - actualExp - 1; RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7); v = v * kPow10[adjustment]; - if (length + adjustment > 19) // has more digits than decimal digits in 64-bit + if (length + static_cast(adjustment)> 19u) // has more digits than decimal digits in 64-bit error += kUlp / 2; } @@ -178,10 +177,10 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit v = v.Normalize(); error <<= oldExp - v.e; - const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); - unsigned precisionSize = 64 - effectiveSignificandSize; + const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + int precisionSize = 64 - effectiveSignificandSize; if (precisionSize + kUlpShift >= 64) { - unsigned scaleExp = (precisionSize + kUlpShift) - 63; + int scaleExp = (precisionSize + kUlpShift) - 63; v.f >>= scaleExp; v.e += scaleExp; error = (error >> scaleExp) + 1 + kUlp; @@ -191,7 +190,7 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; - if (precisionBits >= halfWay + error) { + if (precisionBits >= halfWay + static_cast(error)) { rounded.f++; if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340) rounded.f >>= 1; @@ -201,12 +200,12 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit *result = rounded.ToDouble(); - return halfWay - error >= precisionBits || precisionBits >= halfWay + error; + return halfWay - static_cast(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); } inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) { const BigInteger dInt(decimals, length); - const int dExp = (int)decimalPosition - (int)length + exp; + const int dExp = static_cast(decimalPosition) - static_cast(length) + exp; Double a(approx); int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); if (cmp < 0) @@ -246,10 +245,10 @@ inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t // Trim right-most digits const int kMaxDecimalDigit = 780; - if ((int)length > kMaxDecimalDigit) { - int delta = (int(length) - kMaxDecimalDigit); + if (static_cast(length) > kMaxDecimalDigit) { + int delta = (static_cast(length) - kMaxDecimalDigit); exp += delta; - decimalPosition -= delta; + decimalPosition -= static_cast(delta); length = kMaxDecimalDigit; } diff --git a/contrib/rapidjson/include/rapidjson/internal/swap.h b/contrib/rapidjson/include/rapidjson/internal/swap.h index 0590921f1..666e49f97 100644 --- a/contrib/rapidjson/include/rapidjson/internal/swap.h +++ b/contrib/rapidjson/include/rapidjson/internal/swap.h @@ -17,6 +17,11 @@ #include "../rapidjson.h" +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + RAPIDJSON_NAMESPACE_BEGIN namespace internal { @@ -34,4 +39,8 @@ inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT { } // namespace internal RAPIDJSON_NAMESPACE_END +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + #endif // RAPIDJSON_INTERNAL_SWAP_H_ diff --git a/contrib/rapidjson/include/rapidjson/istreamwrapper.h b/contrib/rapidjson/include/rapidjson/istreamwrapper.h new file mode 100644 index 000000000..8639c8c3c --- /dev/null +++ b/contrib/rapidjson/include/rapidjson/istreamwrapper.h @@ -0,0 +1,115 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ISTREAMWRAPPER_H_ +#define RAPIDJSON_ISTREAMWRAPPER_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::istringstream + - \c std::stringstream + - \c std::wistringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wifstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_istream. +*/ + +template +class BasicIStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + BasicIStreamWrapper(StreamType& stream) : stream_(stream), count_(), peekBuffer_() {} + + Ch Peek() const { + typename StreamType::int_type c = stream_.peek(); + return RAPIDJSON_LIKELY(c != StreamType::traits_type::eof()) ? static_cast(c) : static_cast('\0'); + } + + Ch Take() { + typename StreamType::int_type c = stream_.get(); + if (RAPIDJSON_LIKELY(c != StreamType::traits_type::eof())) { + count_++; + return static_cast(c); + } + else + return '\0'; + } + + // tellg() may return -1 when failed. So we count by ourself. + size_t Tell() const { return count_; } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + RAPIDJSON_ASSERT(sizeof(Ch) == 1); // Only usable for byte stream. + int i; + bool hasError = false; + for (i = 0; i < 4; ++i) { + typename StreamType::int_type c = stream_.get(); + if (c == StreamType::traits_type::eof()) { + hasError = true; + stream_.clear(); + break; + } + peekBuffer_[i] = static_cast(c); + } + for (--i; i >= 0; --i) + stream_.putback(peekBuffer_[i]); + return !hasError ? peekBuffer_ : 0; + } + +private: + BasicIStreamWrapper(const BasicIStreamWrapper&); + BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); + + StreamType& stream_; + size_t count_; //!< Number of characters read. Note: + mutable Ch peekBuffer_[4]; +}; + +typedef BasicIStreamWrapper IStreamWrapper; +typedef BasicIStreamWrapper WIStreamWrapper; + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ISTREAMWRAPPER_H_ diff --git a/contrib/rapidjson/include/rapidjson/memorybuffer.h b/contrib/rapidjson/include/rapidjson/memorybuffer.h index 2484b2185..39bee1dec 100644 --- a/contrib/rapidjson/include/rapidjson/memorybuffer.h +++ b/contrib/rapidjson/include/rapidjson/memorybuffer.h @@ -15,7 +15,7 @@ #ifndef RAPIDJSON_MEMORYBUFFER_H_ #define RAPIDJSON_MEMORYBUFFER_H_ -#include "rapidjson.h" +#include "stream.h" #include "internal/stack.h" RAPIDJSON_NAMESPACE_BEGIN diff --git a/contrib/rapidjson/include/rapidjson/memorystream.h b/contrib/rapidjson/include/rapidjson/memorystream.h index 99feae5d7..1d71d8a4f 100644 --- a/contrib/rapidjson/include/rapidjson/memorystream.h +++ b/contrib/rapidjson/include/rapidjson/memorystream.h @@ -15,7 +15,13 @@ #ifndef RAPIDJSON_MEMORYSTREAM_H_ #define RAPIDJSON_MEMORYSTREAM_H_ -#include "rapidjson.h" +#include "stream.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif RAPIDJSON_NAMESPACE_BEGIN @@ -36,8 +42,8 @@ struct MemoryStream { MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} - Ch Peek() const { return (src_ == end_) ? '\0' : *src_; } - Ch Take() { return (src_ == end_) ? '\0' : *src_++; } + Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; } + Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; } size_t Tell() const { return static_cast(src_ - begin_); } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } @@ -58,4 +64,8 @@ struct MemoryStream { RAPIDJSON_NAMESPACE_END +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + #endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/contrib/rapidjson/include/rapidjson/msinttypes/stdint.h b/contrib/rapidjson/include/rapidjson/msinttypes/stdint.h index a26fff4bf..3d4477b9a 100644 --- a/contrib/rapidjson/include/rapidjson/msinttypes/stdint.h +++ b/contrib/rapidjson/include/rapidjson/msinttypes/stdint.h @@ -89,14 +89,14 @@ #include // For Visual Studio 6 in C++ mode and for many Visual Studio versions when -// compiling for ARM we should wrap include with 'extern "C++" {}' -// or compiler give many errors like this: +// compiling for ARM we have to wrap include with 'extern "C++" {}' +// or compiler would give many errors like this: // error C2733: second C linkage of overloaded function 'wmemchr' not allowed -#ifdef __cplusplus +#if defined(__cplusplus) && !defined(_M_ARM) extern "C" { #endif # include -#ifdef __cplusplus +#if defined(__cplusplus) && !defined(_M_ARM) } #endif diff --git a/contrib/rapidjson/include/rapidjson/ostreamwrapper.h b/contrib/rapidjson/include/rapidjson/ostreamwrapper.h new file mode 100644 index 000000000..6f4667c08 --- /dev/null +++ b/contrib/rapidjson/include/rapidjson/ostreamwrapper.h @@ -0,0 +1,81 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_OSTREAMWRAPPER_H_ +#define RAPIDJSON_OSTREAMWRAPPER_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::ostringstream + - \c std::stringstream + - \c std::wpstringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wofstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_ostream. +*/ + +template +class BasicOStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + BasicOStreamWrapper(StreamType& stream) : stream_(stream) {} + + void Put(Ch c) { + stream_.put(c); + } + + void Flush() { + stream_.flush(); + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + BasicOStreamWrapper(const BasicOStreamWrapper&); + BasicOStreamWrapper& operator=(const BasicOStreamWrapper&); + + StreamType& stream_; +}; + +typedef BasicOStreamWrapper OStreamWrapper; +typedef BasicOStreamWrapper WOStreamWrapper; + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_OSTREAMWRAPPER_H_ diff --git a/contrib/rapidjson/include/rapidjson/pointer.h b/contrib/rapidjson/include/rapidjson/pointer.h index 5d2aa8d63..0f377efec 100644 --- a/contrib/rapidjson/include/rapidjson/pointer.h +++ b/contrib/rapidjson/include/rapidjson/pointer.h @@ -18,6 +18,16 @@ #include "document.h" #include "internal/itoa.h" +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + RAPIDJSON_NAMESPACE_BEGIN static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token @@ -71,7 +81,7 @@ template class GenericPointer { public: typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value - typedef typename EncodingType::Ch Ch; //!< Character type from Value + typedef typename ValueType::Ch Ch; //!< Character type from Value //! A token is the basic units of internal representation. /*! @@ -96,7 +106,7 @@ public: //@{ //! Default constructor. - GenericPointer() : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} //! Constructor that parses a string or URI fragment representation. /*! @@ -155,7 +165,7 @@ public: GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} //! Copy constructor. - GenericPointer(const GenericPointer& rhs) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + GenericPointer(const GenericPointer& rhs, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { *this = rhs; } @@ -230,7 +240,7 @@ public: template RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) Append(T* name, Allocator* allocator = 0) const { - return Append(name, StrLen(name), allocator); + return Append(name, internal::StrLen(name), allocator); } #if RAPIDJSON_HAS_STDSTRING @@ -253,17 +263,18 @@ public: */ GenericPointer Append(SizeType index, Allocator* allocator = 0) const { char buffer[21]; - SizeType length = (sizeof(SizeType) == 4 ? internal::u32toa(index, buffer): internal::u64toa(index, buffer)) - buffer; + char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer); + SizeType length = static_cast(end - buffer); buffer[length] = '\0'; if (sizeof(Ch) == 1) { - Token token = { (Ch*)buffer, length, index }; + Token token = { reinterpret_cast(buffer), length, index }; return Append(token, allocator); } else { Ch name[21]; for (size_t i = 0; i <= length; i++) - name[i] = buffer[i]; + name[i] = static_cast(buffer[i]); Token token = { name, length, index }; return Append(token, allocator); } @@ -271,7 +282,7 @@ public: //! Append a token by value, and return a new Pointer /*! - \param value Value (either Uint or String) to be appended. + \param token token to be appended. \param allocator Allocator for the newly return Pointer. \return A new Pointer with appended token. */ @@ -299,6 +310,9 @@ public: //@} + //! Get the allocator of this pointer. + Allocator& GetAllocator() { return *allocator_; } + //!@name Tokens //@{ @@ -390,7 +404,7 @@ public: bool exist = true; for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { if (v->IsArray() && t->name[0] == '-' && t->length == 1) { - v->PushBack(Value().Move(), allocator); + v->PushBack(ValueType().Move(), allocator); v = &((*v)[v->Size() - 1]); exist = false; } @@ -408,7 +422,7 @@ public: if (t->index >= v->Size()) { v->Reserve(t->index + 1, allocator); while (t->index >= v->Size()) - v->PushBack(Value().Move(), allocator); + v->PushBack(ValueType().Move(), allocator); exist = false; } v = &((*v)[t->index]); @@ -416,7 +430,7 @@ public: else { typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); if (m == v->MemberEnd()) { - v->AddMember(Value(t->name, t->length, allocator).Move(), Value().Move(), allocator); + v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); v = &(--v->MemberEnd())->value; // Assumes AddMember() appends at the end exist = false; } @@ -435,7 +449,6 @@ public: //! Creates a value in a document. /*! \param document A document to be resolved. - \param allocator Allocator for creating the values if the specified value or its parents are not exist. \param alreadyExist If non-null, it stores whether the resolved value is already exist. \return The resolved newly created, or already exists value. */ @@ -452,9 +465,18 @@ public: //! Query a value in a subtree. /*! \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. \return Pointer to the value if it can be resolved. Otherwise null. + + \note + There are only 3 situations when a value cannot be resolved: + 1. A value in the path is not an array nor object. + 2. An object value does not contain the token. + 3. A token is out of range of an array value. + + Use unresolvedTokenIndex to retrieve the token index. */ - ValueType* Get(ValueType& root) const { + ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const { RAPIDJSON_ASSERT(IsValid()); ValueType* v = &root; for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { @@ -463,18 +485,23 @@ public: { typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); if (m == v->MemberEnd()) - return 0; + break; v = &m->value; } - break; + continue; case kArrayType: if (t->index == kPointerInvalidIndex || t->index >= v->Size()) - return 0; + break; v = &((*v)[t->index]); - break; + continue; default: - return 0; + break; } + + // Error: unresolved token + if (unresolvedTokenIndex) + *unresolvedTokenIndex = static_cast(t - tokens_); + return 0; } return v; } @@ -484,7 +511,9 @@ public: \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. \return Pointer to the value if it can be resolved. Otherwise null. */ - const ValueType* Get(const ValueType& root) const { return Get(const_cast(root)); } + const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const { + return Get(const_cast(root), unresolvedTokenIndex); + } //@} @@ -525,7 +554,7 @@ public: //! Query a value in a subtree with default primitive value. /*! - \tparam T \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) @@ -555,7 +584,7 @@ public: //! Query a value in a document with default primitive value. /*! - \tparam T \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) @@ -601,7 +630,7 @@ public: //! Set a primitive value in a subtree. /*! - \tparam T \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) @@ -637,7 +666,7 @@ public: //! Set a primitive value in a document. /*! - \tparam T \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) @@ -729,7 +758,7 @@ private: */ Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { if (!allocator_) // allocator is independently owned. - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) @@ -738,8 +767,12 @@ private: tokenCount_ = rhs.tokenCount_ + extraToken; tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch))); nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); - std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); - std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); + if (rhs.tokenCount_ > 0) { + std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); + } + if (nameBufferSize > 0) { + std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); + } // Adjust pointers to name buffer std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_; @@ -759,11 +792,13 @@ private: } //! Parse a JSON String or its URI fragment representation into tokens. +#ifndef __clang__ // -Wdocumentation /*! \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated. \param length Length of the source string. \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped. */ +#endif void Parse(const Ch* source, size_t length) { RAPIDJSON_ASSERT(source != NULL); RAPIDJSON_ASSERT(nameBuffer_ == 0); @@ -771,7 +806,7 @@ private: // Create own allocator if user did not supply. if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); // Count number of '/' as tokenCount tokenCount_ = 0; @@ -857,7 +892,7 @@ private: *name++ = c; } - token->length = name - token->name; + token->length = static_cast(name - token->name); if (token->length == 0) isNumber = false; *name++ = '\0'; // Null terminator @@ -944,6 +979,8 @@ private: */ class PercentDecodeStream { public: + typedef typename ValueType::Ch Ch; + //! Constructor /*! \param source Start of the stream @@ -959,11 +996,11 @@ private: src_++; Ch c = 0; for (int j = 0; j < 2; j++) { - c <<= 4; + c = static_cast(c << 4); Ch h = *src_; - if (h >= '0' && h <= '9') c += h - '0'; - else if (h >= 'A' && h <= 'F') c += h - 'A' + 10; - else if (h >= 'a' && h <= 'f') c += h - 'a' + 10; + if (h >= '0' && h <= '9') c = static_cast(c + h - '0'); + else if (h >= 'A' && h <= 'F') c = static_cast(c + h - 'A' + 10); + else if (h >= 'a' && h <= 'f') c = static_cast(c + h - 'a' + 10); else { valid_ = false; return 0; @@ -973,7 +1010,7 @@ private: return c; } - size_t Tell() const { return src_ - head_; } + size_t Tell() const { return static_cast(src_ - head_); } bool IsValid() const { return valid_; } private: @@ -992,8 +1029,8 @@ private: unsigned char u = static_cast(c); static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; os_.Put('%'); - os_.Put(hexDigits[u >> 4]); - os_.Put(hexDigits[u & 15]); + os_.Put(static_cast(hexDigits[u >> 4])); + os_.Put(static_cast(hexDigits[u & 15])); } private: OutputStream& os_; @@ -1041,23 +1078,23 @@ typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, c ////////////////////////////////////////////////////////////////////////////// template -typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer) { - return pointer.Get(root); +typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); } template -const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer) { - return pointer.Get(root); +const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); } template -typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N]) { - return GenericPointer(source, N - 1).Get(root); +typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); } template -const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N]) { - return GenericPointer(source, N - 1).Get(root); +const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); } ////////////////////////////////////////////////////////////////////////////// @@ -1310,4 +1347,12 @@ bool EraseValueByPointer(T& root, const CharType(&source)[N]) { RAPIDJSON_NAMESPACE_END +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + #endif // RAPIDJSON_POINTER_H_ diff --git a/contrib/rapidjson/include/rapidjson/prettywriter.h b/contrib/rapidjson/include/rapidjson/prettywriter.h index 416dd492e..98dfb3060 100644 --- a/contrib/rapidjson/include/rapidjson/prettywriter.h +++ b/contrib/rapidjson/include/rapidjson/prettywriter.h @@ -22,8 +22,21 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + RAPIDJSON_NAMESPACE_BEGIN +//! Combination of PrettyWriter format flags. +/*! \see PrettyWriter::SetFormatOptions + */ +enum PrettyFormatOptions { + kFormatDefault = 0, //!< Default pretty formatting. + kFormatSingleLineArray = 1 //!< Format arrays on a single line. +}; + //! Writer with indentation and spacing. /*! \tparam OutputStream Type of ouptut os. @@ -31,10 +44,10 @@ RAPIDJSON_NAMESPACE_BEGIN \tparam TargetEncoding Encoding of output stream. \tparam StackAllocator Type of allocator for allocating memory of stack. */ -template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator> -class PrettyWriter : public Writer { +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class PrettyWriter : public Writer { public: - typedef Writer Base; + typedef Writer Base; typedef typename Base::Ch Ch; //! Constructor @@ -42,8 +55,17 @@ public: \param allocator User supplied allocator. If it is null, it will create a private one. \param levelDepth Initial capacity of stack. */ - PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : - Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} + explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} + + + explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + PrettyWriter(PrettyWriter&& rhs) : + Base(std::forward(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {} +#endif //! Set custom indentation. /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). @@ -57,6 +79,14 @@ public: return *this; } + //! Set pretty writer formatting options. + /*! \param options Formatting options. + */ + PrettyWriter& SetFormatOptions(PrettyFormatOptions options) { + formatOptions_ = options; + return *this; + } + /*! @name Implementation of Handler \see Handler */ @@ -70,7 +100,15 @@ public: bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + PrettyPrefix(kNumberType); + return Base::WriteString(str, length); + } + bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); (void)copy; PrettyPrefix(kStringType); return Base::WriteString(str, length); @@ -89,11 +127,19 @@ public: } bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) { + return Key(str.data(), SizeType(str.size())); + } +#endif bool EndObject(SizeType memberCount = 0) { (void)memberCount; - RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); - RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; if (!empty) { @@ -104,7 +150,7 @@ public: (void)ret; RAPIDJSON_ASSERT(ret == true); if (Base::level_stack_.Empty()) // end of json text - Base::os_->Flush(); + Base::Flush(); return true; } @@ -120,7 +166,7 @@ public: RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; - if (!empty) { + if (!empty && !(formatOptions_ & kFormatSingleLineArray)) { Base::os_->Put('\n'); WriteIndent(); } @@ -128,7 +174,7 @@ public: (void)ret; RAPIDJSON_ASSERT(ret == true); if (Base::level_stack_.Empty()) // end of json text - Base::os_->Flush(); + Base::Flush(); return true; } @@ -142,6 +188,22 @@ public: bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. + */ + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + PrettyPrefix(type); + return Base::WriteRawValue(json, length); + } + protected: void PrettyPrefix(Type type) { (void)type; @@ -151,11 +213,14 @@ protected: if (level->inArray) { if (level->valueCount > 0) { Base::os_->Put(','); // add comma if it is not the first element in array - Base::os_->Put('\n'); + if (formatOptions_ & kFormatSingleLineArray) + Base::os_->Put(' '); } - else + + if (!(formatOptions_ & kFormatSingleLineArray)) { Base::os_->Put('\n'); - WriteIndent(); + WriteIndent(); + } } else { // in object if (level->valueCount > 0) { @@ -186,11 +251,12 @@ protected: void WriteIndent() { size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; - PutN(*Base::os_, indentChar_, count); + PutN(*Base::os_, static_cast(indentChar_), count); } Ch indentChar_; unsigned indentCharCount_; + PrettyFormatOptions formatOptions_; private: // Prohibit copy constructor & assignment operator. @@ -200,6 +266,10 @@ private: RAPIDJSON_NAMESPACE_END +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif diff --git a/contrib/rapidjson/include/rapidjson/rapidjson.h b/contrib/rapidjson/include/rapidjson/rapidjson.h index f22130d3c..5716fdc06 100644 --- a/contrib/rapidjson/include/rapidjson/rapidjson.h +++ b/contrib/rapidjson/include/rapidjson/rapidjson.h @@ -49,6 +49,11 @@ // token stringification #define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) #define RAPIDJSON_DO_STRINGIFY(x) #x + +// token concatenation +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y //!@endcond /*! \def RAPIDJSON_MAJOR_VERSION @@ -68,8 +73,8 @@ \brief Version of RapidJSON in ".." string format. */ #define RAPIDJSON_MAJOR_VERSION 1 -#define RAPIDJSON_MINOR_VERSION 0 -#define RAPIDJSON_PATCH_VERSION 2 +#define RAPIDJSON_MINOR_VERSION 1 +#define RAPIDJSON_PATCH_VERSION 0 #define RAPIDJSON_VERSION_STRING \ RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION) @@ -119,6 +124,31 @@ #define RAPIDJSON_NAMESPACE_END } #endif +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_HAS_STDSTRING + +#ifndef RAPIDJSON_HAS_STDSTRING +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation +#else +#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default +#endif +/*! \def RAPIDJSON_HAS_STDSTRING + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for \c std::string + + By defining this preprocessor symbol to \c 1, several convenience functions for using + \ref rapidjson::GenericValue with \c std::string are enabled, especially + for construction and comparison. + + \hideinitializer +*/ +#endif // !defined(RAPIDJSON_HAS_STDSTRING) + +#if RAPIDJSON_HAS_STDSTRING +#include +#endif // RAPIDJSON_HAS_STDSTRING + /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_NO_INT64DEFINE @@ -134,7 +164,7 @@ */ #ifndef RAPIDJSON_NO_INT64DEFINE //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#ifdef _MSC_VER +#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013 #include "msinttypes/stdint.h" #include "msinttypes/inttypes.h" #else @@ -153,9 +183,9 @@ #ifndef RAPIDJSON_FORCEINLINE //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#if defined(_MSC_VER) && !defined(NDEBUG) +#if defined(_MSC_VER) && defined(NDEBUG) #define RAPIDJSON_FORCEINLINE __forceinline -#elif defined(__GNUC__) && __GNUC__ >= 4 && !defined(NDEBUG) +#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG) #define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) #else #define RAPIDJSON_FORCEINLINE @@ -211,6 +241,8 @@ # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64)) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN # elif defined(RAPIDJSON_DOXYGEN_RUNNING) # define RAPIDJSON_ENDIAN # else @@ -223,7 +255,7 @@ //! Whether using 64-bit architecture #ifndef RAPIDJSON_64BIT -#if defined(__LP64__) || defined(_WIN64) +#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__) #define RAPIDJSON_64BIT 1 #else #define RAPIDJSON_64BIT 0 @@ -238,13 +270,14 @@ \param x pointer to align Some machines require strict data alignment. Currently the default uses 4 bytes - alignment. User can customize by defining the RAPIDJSON_ALIGN function macro., + alignment on 32-bit platforms and 8 bytes alignment for 64-bit platforms. + User can customize by defining the RAPIDJSON_ALIGN function macro. */ #ifndef RAPIDJSON_ALIGN #if RAPIDJSON_64BIT == 1 -#define RAPIDJSON_ALIGN(x) ((x + 7u) & ~7u) +#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) #else -#define RAPIDJSON_ALIGN(x) ((x + 3u) & ~3u) +#define RAPIDJSON_ALIGN(x) (((x) + 3u) & ~3u) #endif #endif @@ -262,17 +295,47 @@ #endif /////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD +// RAPIDJSON_48BITPOINTER_OPTIMIZATION + +//! Use only lower 48-bit address for some pointers. +/*! + \ingroup RAPIDJSON_CONFIG + + This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address. + The higher 16-bit can be used for storing other data. + \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture. +*/ +#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1 +#else +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 +#endif +#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION + +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1 +#if RAPIDJSON_64BIT != 1 +#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1 +#endif +#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast((reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast(reinterpret_cast(x)))) +#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast(reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF)))) +#else +#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x)) +#define RAPIDJSON_GETPOINTER(type, p) (p) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD /*! \def RAPIDJSON_SIMD \ingroup RAPIDJSON_CONFIG - \brief Enable SSE2/SSE4.2 optimization. + \brief Enable SSE2/SSE4.2/Neon optimization. RapidJSON supports optimized implementations for some parsing operations - based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible - processors. + based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel + or ARM compatible processors. - To enable these optimizations, two different symbols can be defined; + To enable these optimizations, three different symbols can be defined; \code // Enable SSE2 optimization. #define RAPIDJSON_SSE2 @@ -281,13 +344,17 @@ #define RAPIDJSON_SSE42 \endcode - \c RAPIDJSON_SSE42 takes precedence, if both are defined. + // Enable ARM Neon optimization. + #define RAPIDJSON_NEON + \endcode + + \c RAPIDJSON_SSE42 takes precedence over SSE2, if both are defined. If any of these symbols is defined, RapidJSON defines the macro \c RAPIDJSON_SIMD to indicate the availability of the optimized code. */ #if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ - || defined(RAPIDJSON_DOXYGEN_RUNNING) + || defined(RAPIDJSON_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING) #define RAPIDJSON_SIMD #endif @@ -347,25 +414,33 @@ RAPIDJSON_NAMESPACE_END /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_STATIC_ASSERT -// Adopt from boost +// Prefer C++11 static_assert, if available #ifndef RAPIDJSON_STATIC_ASSERT +#if __cplusplus >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 ) +#define RAPIDJSON_STATIC_ASSERT(x) \ + static_assert(x, RAPIDJSON_STRINGIFY(x)) +#endif // C++11 +#endif // RAPIDJSON_STATIC_ASSERT + +// Adopt C++03 implementation from boost +#ifndef RAPIDJSON_STATIC_ASSERT +#ifndef __clang__ //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#endif RAPIDJSON_NAMESPACE_BEGIN template struct STATIC_ASSERTION_FAILURE; template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; -template struct StaticAssertTest {}; +template struct StaticAssertTest {}; RAPIDJSON_NAMESPACE_END -#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) -#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) -#define RAPIDJSON_DO_JOIN2(X, Y) X##Y - #if defined(__GNUC__) #define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) #else #define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE #endif +#ifndef __clang__ //!@endcond +#endif /*! \def RAPIDJSON_STATIC_ASSERT \brief (Internal) macro to check for conditions at compile-time @@ -376,6 +451,35 @@ RAPIDJSON_NAMESPACE_END typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif // RAPIDJSON_STATIC_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY + +//! Compiler branching hint for expression with high probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression likely to be true. +*/ +#ifndef RAPIDJSON_LIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define RAPIDJSON_LIKELY(x) (x) +#endif +#endif + +//! Compiler branching hint for expression with low probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression unlikely to be true. +*/ +#ifndef RAPIDJSON_UNLIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define RAPIDJSON_UNLIKELY(x) (x) +#endif #endif /////////////////////////////////////////////////////////////////////////////// @@ -438,8 +542,12 @@ RAPIDJSON_NAMESPACE_END #ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS #if defined(__clang__) -#define RAPIDJSON_HAS_CXX11_RVALUE_REFS __has_feature(cxx_rvalue_references) && \ +#if __has_feature(cxx_rvalue_references) && \ (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif #elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ (defined(_MSC_VER) && _MSC_VER >= 1600) @@ -470,6 +578,17 @@ RAPIDJSON_NAMESPACE_END #define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 #endif +#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 +#else +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR + //!@endcond /////////////////////////////////////////////////////////////////////////////// @@ -477,7 +596,7 @@ RAPIDJSON_NAMESPACE_END #ifndef RAPIDJSON_NEW ///! customization point for global \c new -#define RAPIDJSON_NEW(x) new x +#define RAPIDJSON_NEW(TypeName) new TypeName #endif #ifndef RAPIDJSON_DELETE ///! customization point for global \c delete @@ -485,10 +604,7 @@ RAPIDJSON_NAMESPACE_END #endif /////////////////////////////////////////////////////////////////////////////// -// Allocators and Encodings - -#include "allocators.h" -#include "encodings.h" +// Type /*! \namespace rapidjson \brief main RapidJSON namespace @@ -496,148 +612,6 @@ RAPIDJSON_NAMESPACE_END */ RAPIDJSON_NAMESPACE_BEGIN -/////////////////////////////////////////////////////////////////////////////// -// Stream - -/*! \class rapidjson::Stream - \brief Concept for reading and writing characters. - - For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). - - For write-only stream, only need to implement Put() and Flush(). - -\code -concept Stream { - typename Ch; //!< Character type of the stream. - - //! Read the current character from stream without moving the read cursor. - Ch Peek() const; - - //! Read the current character from stream and moving the read cursor to next character. - Ch Take(); - - //! Get the current read cursor. - //! \return Number of characters read from start. - size_t Tell(); - - //! Begin writing operation at the current read pointer. - //! \return The begin writer pointer. - Ch* PutBegin(); - - //! Write a character. - void Put(Ch c); - - //! Flush the buffer. - void Flush(); - - //! End the writing operation. - //! \param begin The begin write pointer returned by PutBegin(). - //! \return Number of characters written. - size_t PutEnd(Ch* begin); -} -\endcode -*/ - -//! Provides additional information for stream. -/*! - By using traits pattern, this type provides a default configuration for stream. - For custom stream, this type can be specialized for other configuration. - See TEST(Reader, CustomStringStream) in readertest.cpp for example. -*/ -template -struct StreamTraits { - //! Whether to make local copy of stream for optimization during parsing. - /*! - By default, for safety, streams do not use local copy optimization. - Stream that can be copied fast should specialize this, like StreamTraits. - */ - enum { copyOptimization = 0 }; -}; - -//! Put N copies of a character to a stream. -template -inline void PutN(Stream& stream, Ch c, size_t n) { - for (size_t i = 0; i < n; i++) - stream.Put(c); -} - -/////////////////////////////////////////////////////////////////////////////// -// StringStream - -//! Read-only string stream. -/*! \note implements Stream concept -*/ -template -struct GenericStringStream { - typedef typename Encoding::Ch Ch; - - GenericStringStream(const Ch *src) : src_(src), head_(src) {} - - Ch Peek() const { return *src_; } - Ch Take() { return *src_++; } - size_t Tell() const { return static_cast(src_ - head_); } - - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - void Put(Ch) { RAPIDJSON_ASSERT(false); } - void Flush() { RAPIDJSON_ASSERT(false); } - size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } - - const Ch* src_; //!< Current read position. - const Ch* head_; //!< Original head of the string. -}; - -template -struct StreamTraits > { - enum { copyOptimization = 1 }; -}; - -//! String stream with UTF8 encoding. -typedef GenericStringStream > StringStream; - -/////////////////////////////////////////////////////////////////////////////// -// InsituStringStream - -//! A read-write string stream. -/*! This string stream is particularly designed for in-situ parsing. - \note implements Stream concept -*/ -template -struct GenericInsituStringStream { - typedef typename Encoding::Ch Ch; - - GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} - - // Read - Ch Peek() { return *src_; } - Ch Take() { return *src_++; } - size_t Tell() { return static_cast(src_ - head_); } - - // Write - void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } - - Ch* PutBegin() { return dst_ = src_; } - size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } - void Flush() {} - - Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } - void Pop(size_t count) { dst_ -= count; } - - Ch* src_; - Ch* dst_; - Ch* head_; -}; - -template -struct StreamTraits > { - enum { copyOptimization = 1 }; -}; - -//! Insitu string stream with UTF8 encoding. -typedef GenericInsituStringStream > InsituStringStream; - -/////////////////////////////////////////////////////////////////////////////// -// Type - //! Type of JSON value enum Type { kNullType = 0, //!< null diff --git a/contrib/rapidjson/include/rapidjson/reader.h b/contrib/rapidjson/include/rapidjson/reader.h index c5ecf4be5..120c31115 100644 --- a/contrib/rapidjson/include/rapidjson/reader.h +++ b/contrib/rapidjson/include/rapidjson/reader.h @@ -1,5 +1,5 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// +// // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except @@ -7,9 +7,9 @@ // // http://opensource.org/licenses/MIT // -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_READER_H_ @@ -17,11 +17,13 @@ /*! \file reader.h */ -#include "rapidjson.h" -#include "encodings.h" +#include "allocators.h" +#include "stream.h" +#include "encodedstream.h" #include "internal/meta.h" #include "internal/stack.h" #include "internal/strtod.h" +#include #if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) #include @@ -31,6 +33,8 @@ #include #elif defined(RAPIDJSON_SSE2) #include +#elif defined(RAPIDJSON_NEON) +#include #endif #ifdef _MSC_VER @@ -39,6 +43,13 @@ RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant RAPIDJSON_DIAG_OFF(4702) // unreachable code #endif +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(old-style-cast) +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +#endif + #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) @@ -49,7 +60,7 @@ RAPIDJSON_DIAG_OFF(effc++) #ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN #define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ RAPIDJSON_MULTILINEMACRO_BEGIN \ - if (HasParseError()) { return value; } \ + if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \ RAPIDJSON_MULTILINEMACRO_END #endif #define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ @@ -120,7 +131,7 @@ RAPIDJSON_NAMESPACE_BEGIN /////////////////////////////////////////////////////////////////////////////// // ParseFlag -/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS +/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS \ingroup RAPIDJSON_CONFIG \brief User-defined kParseDefaultFlags definition. @@ -140,6 +151,10 @@ enum ParseFlag { kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). + kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments. + kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings. + kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays. + kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles. kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS }; @@ -148,7 +163,7 @@ enum ParseFlag { /*! \class rapidjson::Handler \brief Concept for receiving events from GenericReader upon parsing. - The functions return true if no error occurs. If they return false, + The functions return true if no error occurs. If they return false, the event publisher should terminate the process. \code concept Handler { @@ -161,6 +176,8 @@ concept Handler { bool Int64(int64_t i); bool Uint64(uint64_t i); bool Double(double d); + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType length, bool copy); bool String(const Ch* str, SizeType length, bool copy); bool StartObject(); bool Key(const Ch* str, SizeType length, bool copy); @@ -191,6 +208,8 @@ struct BaseReaderHandler { bool Int64(int64_t) { return static_cast(*this).Default(); } bool Uint64(uint64_t) { return static_cast(*this).Default(); } bool Double(double) { return static_cast(*this).Default(); } + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } bool StartObject() { return static_cast(*this).Default(); } bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } @@ -248,10 +267,17 @@ void SkipWhitespace(InputStream& is) { internal::StreamLocalCopy copy(is); InputStream& s(copy.s); - while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t') + typename InputStream::Ch c; + while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t') s.Take(); } +inline const char* SkipWhitespace(const char* p, const char* end) { + while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + return p; +} + #ifdef RAPIDJSON_SSE42 //! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. inline const char *SkipWhitespace_SIMD(const char* p) { @@ -262,7 +288,7 @@ inline const char *SkipWhitespace_SIMD(const char* p) { return p; // 16-byte align to the next boundary - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & ~15); + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); while (p != nextAligned) if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') ++p; @@ -271,23 +297,37 @@ inline const char *SkipWhitespace_SIMD(const char* p) { // The rest of string using SIMD static const char whitespace[16] = " \n\r\t"; - const __m128i w = _mm_load_si128((const __m128i *)&whitespace[0]); + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); for (;; p += 16) { - const __m128i s = _mm_load_si128((const __m128i *)p); - const unsigned r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); - if (r != 0) { // some of characters is non-whitespace -#ifdef _MSC_VER // Find the index of first non-whitespace - unsigned long offset; - _BitScanForward(&offset, r); - return p + offset; -#else - return p + __builtin_ffs(r) - 1; -#endif - } + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; } } +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The middle of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; + } + + return SkipWhitespace(p, end); +} + #elif defined(RAPIDJSON_SSE2) //! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. @@ -299,7 +339,7 @@ inline const char *SkipWhitespace_SIMD(const char* p) { return p; // 16-byte align to the next boundary - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & ~15); + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); while (p != nextAligned) if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') ++p; @@ -307,24 +347,22 @@ inline const char *SkipWhitespace_SIMD(const char* p) { return p; // The rest of string - static const char whitespaces[4][17] = { - " ", - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", - "\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"}; + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 - const __m128i w0 = _mm_loadu_si128((const __m128i *)&whitespaces[0][0]); - const __m128i w1 = _mm_loadu_si128((const __m128i *)&whitespaces[1][0]); - const __m128i w2 = _mm_loadu_si128((const __m128i *)&whitespaces[2][0]); - const __m128i w3 = _mm_loadu_si128((const __m128i *)&whitespaces[3][0]); + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); for (;; p += 16) { - const __m128i s = _mm_load_si128((const __m128i *)p); + const __m128i s = _mm_load_si128(reinterpret_cast(p)); __m128i x = _mm_cmpeq_epi8(s, w0); x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); - unsigned short r = (unsigned short)~_mm_movemask_epi8(x); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); if (r != 0) { // some of characters may be non-whitespace #ifdef _MSC_VER // Find the index of first non-whitespace unsigned long offset; @@ -337,11 +375,134 @@ inline const char *SkipWhitespace_SIMD(const char* p) { } } -#endif // RAPIDJSON_SSE2 +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } + + return SkipWhitespace(p, end); +} + +#elif defined(RAPIDJSON_NEON) + +//! Skip whitespace with ARM Neon instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + if (low == 0) { + if (high != 0) { + int lz =__builtin_clzll(high);; + return p + 8 + (lz >> 3); + } + } else { + int lz = __builtin_clzll(low);; + return p + (lz >> 3); + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (; p <= end - 16; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + if (low == 0) { + if (high != 0) { + int lz = __builtin_clzll(high); + return p + 8 + (lz >> 3); + } + } else { + int lz = __builtin_clzll(low); + return p + (lz >> 3); + } + } + + return SkipWhitespace(p, end); +} + +#endif // RAPIDJSON_NEON #ifdef RAPIDJSON_SIMD //! Template function specialization for InsituStringStream -template<> inline void SkipWhitespace(InsituStringStream& is) { +template<> inline void SkipWhitespace(InsituStringStream& is) { is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); } @@ -349,23 +510,27 @@ template<> inline void SkipWhitespace(InsituStringStream& is) { template<> inline void SkipWhitespace(StringStream& is) { is.src_ = SkipWhitespace_SIMD(is.src_); } + +template<> inline void SkipWhitespace(EncodedInputStream, MemoryStream>& is) { + is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_); +} #endif // RAPIDJSON_SIMD /////////////////////////////////////////////////////////////////////////////// // GenericReader //! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. -/*! GenericReader parses JSON text from a stream, and send events synchronously to an +/*! GenericReader parses JSON text from a stream, and send events synchronously to an object implementing Handler concept. - It needs to allocate a stack for storing a single decoded string during + It needs to allocate a stack for storing a single decoded string during non-destructive parsing. - For in-situ parsing, the decoded string is directly written to the source + For in-situ parsing, the decoded string is directly written to the source text string, no temporary buffer is required. A GenericReader object can be reused for parsing multiple JSON text. - + \tparam SourceEncoding Encoding of the input stream. \tparam TargetEncoding Encoding of the parse output. \tparam StackAllocator Allocator type for stack. @@ -398,9 +563,10 @@ public: ClearStackOnExit scope(*this); - SkipWhitespace(is); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); - if (is.Peek() == '\0') { + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); } @@ -409,9 +575,10 @@ public: RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); if (!(parseFlags & kParseStopWhenDoneFlag)) { - SkipWhitespace(is); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); - if (is.Peek() != '\0') { + if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); } @@ -433,9 +600,86 @@ public: return Parse(is, handler); } + //! Initialize JSON text token-by-token parsing + /*! + */ + void IterativeParseInit() { + parseResult_.Clear(); + state_ = IterativeParsingStartState; + } + + //! Parse one token from JSON text + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + bool IterativeParseNext(InputStream& is, Handler& handler) { + while (RAPIDJSON_LIKELY(is.Peek() != '\0')) { + SkipWhitespaceAndComments(is); + + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state_, t); + IterativeParsingState d = Transit(state_, t, n, is, handler); + + // If we've finished or hit an error... + if (RAPIDJSON_UNLIKELY(IsIterativeParsingCompleteState(d))) { + // Report errors. + if (d == IterativeParsingErrorState) { + HandleError(state_, is); + return false; + } + + // Transition to the finish state. + RAPIDJSON_ASSERT(d == IterativeParsingFinishState); + state_ = d; + + // If StopWhenDone is not set... + if (!(parseFlags & kParseStopWhenDoneFlag)) { + // ... and extra non-whitespace data is found... + SkipWhitespaceAndComments(is); + if (is.Peek() != '\0') { + // ... this is considered an error. + HandleError(state_, is); + return false; + } + } + + // Success! We are done! + return true; + } + + // Transition to the new state. + state_ = d; + + // If we parsed anything other than a delimiter, we invoked the handler, so we can return true now. + if (!IsIterativeParsingDelimiterState(n)) + return true; + } + + // We reached the end of file. + stack_.Clear(); + + if (state_ != IterativeParsingFinishState) { + HandleError(state_, is); + return false; + } + + return true; + } + + //! Check if token-by-token parsing JSON text is complete + /*! \return Whether the JSON has been fully decoded. + */ + RAPIDJSON_FORCEINLINE bool IterativeParseComplete() { + return IsIterativeParsingCompleteState(state_); + } + //! Whether a parse error has occured in the last parsing. bool HasParseError() const { return parseResult_.IsError(); } - + //! Get the \ref ParseErrorCode of last parsing. ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } @@ -462,52 +706,98 @@ private: ClearStackOnExit& operator=(const ClearStackOnExit&); }; + template + void SkipWhitespaceAndComments(InputStream& is) { + SkipWhitespace(is); + + if (parseFlags & kParseCommentsFlag) { + while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) { + if (Consume(is, '*')) { + while (true) { + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + else if (Consume(is, '*')) { + if (Consume(is, '/')) + break; + } + else + is.Take(); + } + } + else if (RAPIDJSON_LIKELY(Consume(is, '/'))) + while (is.Peek() != '\0' && is.Take() != '\n') {} + else + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + + SkipWhitespace(is); + } + } + } + // Parse object: { string : value, ... } template void ParseObject(InputStream& is, Handler& handler) { RAPIDJSON_ASSERT(is.Peek() == '{'); is.Take(); // Skip '{' - - if (!handler.StartObject()) + + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - SkipWhitespace(is); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - if (is.Peek() == '}') { - is.Take(); - if (!handler.EndObject(0)) // empty object + if (Consume(is, '}')) { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); return; } for (SizeType memberCount = 0;;) { - if (is.Peek() != '"') + if (RAPIDJSON_UNLIKELY(is.Peek() != '"')) RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); ParseString(is, handler, true); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - SkipWhitespace(is); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - if (is.Take() != ':') + if (RAPIDJSON_UNLIKELY(!Consume(is, ':'))) RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); - SkipWhitespace(is); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; ParseValue(is, handler); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - SkipWhitespace(is); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; ++memberCount; - switch (is.Take()) { - case ',': SkipWhitespace(is); break; - case '}': - if (!handler.EndObject(memberCount)) + switch (is.Peek()) { + case ',': + is.Take(); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + break; + case '}': + is.Take(); + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); return; - default: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); + default: + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy + } + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == '}') { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } } } } @@ -517,15 +807,15 @@ private: void ParseArray(InputStream& is, Handler& handler) { RAPIDJSON_ASSERT(is.Peek() == '['); is.Take(); // Skip '[' - - if (!handler.StartArray()) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - - SkipWhitespace(is); - if (is.Peek() == ']') { - is.Take(); - if (!handler.EndArray(0)) // empty array + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); return; } @@ -535,15 +825,28 @@ private: RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; ++elementCount; - SkipWhitespace(is); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - switch (is.Take()) { - case ',': SkipWhitespace(is); break; - case ']': - if (!handler.EndArray(elementCount)) + if (Consume(is, ',')) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + } + else if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == ']') { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); return; - default: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + } } } } @@ -553,12 +856,12 @@ private: RAPIDJSON_ASSERT(is.Peek() == 'n'); is.Take(); - if (is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l') { - if (!handler.Null()) + if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) { + if (RAPIDJSON_UNLIKELY(!handler.Null())) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); } else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); } template @@ -566,12 +869,12 @@ private: RAPIDJSON_ASSERT(is.Peek() == 't'); is.Take(); - if (is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e') { - if (!handler.Bool(true)) + if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(true))) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); } else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); } template @@ -579,20 +882,30 @@ private: RAPIDJSON_ASSERT(is.Peek() == 'f'); is.Take(); - if (is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e') { - if (!handler.Bool(false)) + if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(false))) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); } else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) { + if (RAPIDJSON_LIKELY(is.Peek() == expect)) { + is.Take(); + return true; + } + else + return false; } // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). template - unsigned ParseHex4(InputStream& is) { + unsigned ParseHex4(InputStream& is, size_t escapeOffset) { unsigned codepoint = 0; for (int i = 0; i < 4; i++) { - Ch c = is.Take(); + Ch c = is.Peek(); codepoint <<= 4; codepoint += static_cast(c); if (c >= '0' && c <= '9') @@ -602,9 +915,10 @@ private: else if (c >= 'a' && c <= 'f') codepoint -= 'a' - 10; else { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); } + is.Take(); } return codepoint; } @@ -619,7 +933,14 @@ private: *stack_.template Push() = c; ++length_; } + + RAPIDJSON_FORCEINLINE void* Push(SizeType count) { + length_ += count; + return stack_.template Push(count); + } + size_t Length() const { return length_; } + Ch* Pop() { return stack_.template Pop(length_); } @@ -638,6 +959,9 @@ private: internal::StreamLocalCopy copy(is); InputStream& s(copy.s); + RAPIDJSON_ASSERT(s.Peek() == '\"'); + s.Take(); // Skip '\"' + bool success = false; if (parseFlags & kParseInsituFlag) { typename InputStream::Ch *head = s.PutBegin(); @@ -645,7 +969,7 @@ private: RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; size_t length = s.PutEnd(head) - 1; RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); - const typename TargetEncoding::Ch* const str = (typename TargetEncoding::Ch*)head; + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); } else { @@ -656,7 +980,7 @@ private: const typename TargetEncoding::Ch* const str = stackStream.Pop(); success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); } - if (!success) + if (RAPIDJSON_UNLIKELY(!success)) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); } @@ -667,74 +991,421 @@ private: //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN #define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 static const char escape[256] = { - Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', - Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, - 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, - 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', + Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, + 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, + 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 }; #undef Z16 //!@endcond - RAPIDJSON_ASSERT(is.Peek() == '\"'); - is.Take(); // Skip '\"' - for (;;) { + // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation. + if (!(parseFlags & kParseValidateEncodingFlag)) + ScanCopyUnescapedString(is, os); + Ch c = is.Peek(); - if (c == '\\') { // Escape + if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape + size_t escapeOffset = is.Tell(); // For invalid escaping, report the inital '\\' as error offset is.Take(); - Ch e = is.Take(); - if ((sizeof(Ch) == 1 || unsigned(e) < 256) && escape[(unsigned char)e]) { - os.Put(escape[(unsigned char)e]); + Ch e = is.Peek(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { + is.Take(); + os.Put(static_cast(escape[static_cast(e)])); } - else if (e == 'u') { // Unicode - unsigned codepoint = ParseHex4(is); + else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode + is.Take(); + unsigned codepoint = ParseHex4(is, escapeOffset); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - if (codepoint >= 0xD800 && codepoint <= 0xDBFF) { + if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDBFF)) { // Handle UTF-16 surrogate pair - if (is.Take() != '\\' || is.Take() != 'u') - RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); - unsigned codepoint2 = ParseHex4(is); + if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + unsigned codepoint2 = ParseHex4(is, escapeOffset); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - if (codepoint2 < 0xDC00 || codepoint2 > 0xDFFF) - RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); + if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; } TEncoding::Encode(os, codepoint); } else - RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); } - else if (c == '"') { // Closing double quote + else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote is.Take(); os.Put('\0'); // null-terminate the string return; } - else if (c == '\0') - RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell() - 1); - else if ((unsigned)c < 0x20) // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF - RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); - else { - if (parseFlags & kParseValidateEncodingFlag ? - !Transcoder::Validate(is, os) : - !Transcoder::Transcode(is, os)) + else if (RAPIDJSON_UNLIKELY(static_cast(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + if (c == '\0') + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); + else RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); } + else { + size_t offset = is.Tell(); + if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? + !Transcoder::Validate(is, os) : + !Transcoder::Transcode(is, os)))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); + } } } - template + template + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) { + // Do nothing for generic version + } + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType length; + #ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; + #else + length = static_cast(__builtin_ffs(r) - 1); + #endif + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16, q += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + for (const char* pend = p + length; p != pend; ) + *q++ = *p++; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + p += length; + break; + } + } + + is.src_ = is.dst_ = p; + } +#elif defined(RAPIDJSON_NEON) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + unsigned lz = (unsigned)__builtin_clzll(high);; + length = 8 + (lz >> 3); + escaped = true; + } + } else { + unsigned lz = (unsigned)__builtin_clzll(low);; + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + vst1q_u8(reinterpret_cast(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16, q += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + unsigned lz = (unsigned)__builtin_clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + unsigned lz = (unsigned)__builtin_clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + for (const char* pend = p + length; p != pend; ) { + *q++ = *p++; + } + break; + } + vst1q_u8(reinterpret_cast(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + if (low == 0) { + if (high != 0) { + int lz = __builtin_clzll(high); + p += 8 + (lz >> 3); + break; + } + } else { + int lz = __builtin_clzll(low); + p += lz >> 3; + break; + } + } + + is.src_ = is.dst_ = p; + } +#endif // RAPIDJSON_NEON + + template class NumberStream; template - class NumberStream { + class NumberStream { public: + typedef typename InputStream::Ch Ch; + NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } - ~NumberStream() {} RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } + RAPIDJSON_FORCEINLINE void Push(char) {} + size_t Tell() { return is.Tell(); } size_t Length() { return 0; } const char* Pop() { return 0; } @@ -746,17 +1417,20 @@ private: }; template - class NumberStream : public NumberStream { - typedef NumberStream Base; + class NumberStream : public NumberStream { + typedef NumberStream Base; public: - NumberStream(GenericReader& reader, InputStream& is) : NumberStream(reader, is), stackStream(reader.stack_) {} - ~NumberStream() {} + NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {} RAPIDJSON_FORCEINLINE Ch TakePush() { - stackStream.Put((char)Base::is.Peek()); + stackStream.Put(static_cast(Base::is.Peek())); return Base::is.Take(); } + RAPIDJSON_FORCEINLINE void Push(char c) { + stackStream.Put(c); + } + size_t Length() { return stackStream.Length(); } const char* Pop() { @@ -768,34 +1442,48 @@ private: StackStream stackStream; }; + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {} + + RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } + }; + template void ParseNumber(InputStream& is, Handler& handler) { internal::StreamLocalCopy copy(is); - NumberStream s(*this, copy.s); + NumberStream s(*this, copy.s); + + size_t startOffset = s.Tell(); + double d = 0.0; + bool useNanOrInf = false; // Parse minus - bool minus = false; - if (s.Peek() == '-') { - minus = true; - s.Take(); - } + bool minus = Consume(s, '-'); // Parse int: zero / ( digit1-9 *DIGIT ) unsigned i = 0; uint64_t i64 = 0; bool use64bit = false; int significandDigit = 0; - if (s.Peek() == '0') { + if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) { i = 0; s.TakePush(); } - else if (s.Peek() >= '1' && s.Peek() <= '9') { + else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) { i = static_cast(s.TakePush() - '0'); if (minus) - while (s.Peek() >= '0' && s.Peek() <= '9') { - if (i >= 214748364) { // 2^31 = 2147483648 - if (i != 214748364 || s.Peek() > '8') { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648 + if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) { i64 = i; use64bit = true; break; @@ -805,9 +1493,9 @@ private: significandDigit++; } else - while (s.Peek() >= '0' && s.Peek() <= '9') { - if (i >= 429496729) { // 2^32 - 1 = 4294967295 - if (i != 429496729 || s.Peek() > '5') { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295 + if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) { i64 = i; use64bit = true; break; @@ -817,18 +1505,41 @@ private: significandDigit++; } } + // Parse NaN or Infinity here + else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { + if (Consume(s, 'N')) { + if (Consume(s, 'a') && Consume(s, 'N')) { + d = std::numeric_limits::quiet_NaN(); + useNanOrInf = true; + } + } + else if (RAPIDJSON_LIKELY(Consume(s, 'I'))) { + if (Consume(s, 'n') && Consume(s, 'f')) { + d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); + useNanOrInf = true; + + if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') + && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } + } + + if (RAPIDJSON_UNLIKELY(!useNanOrInf)) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } else RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); // Parse 64bit int bool useDouble = false; - double d = 0.0; if (use64bit) { - if (minus) - while (s.Peek() >= '0' && s.Peek() <= '9') { - if (i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC)) // 2^63 = 9223372036854775808 - if (i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8') { - d = i64; + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) { + d = static_cast(i64); useDouble = true; break; } @@ -836,10 +1547,10 @@ private: significandDigit++; } else - while (s.Peek() >= '0' && s.Peek() <= '9') { - if (i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999)) // 2^64 - 1 = 18446744073709551615 - if (i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5') { - d = i64; + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) { + d = static_cast(i64); useDouble = true; break; } @@ -850,9 +1561,9 @@ private: // Force double for big integer if (useDouble) { - while (s.Peek() >= '0' && s.Peek() <= '9') { - if (d >= 1.7976931348623157e307) // DBL_MAX / 10.0 - RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(d >= 1.7976931348623157e307)) // DBL_MAX / 10.0 + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); d = d * 10 + (s.TakePush() - '0'); } } @@ -860,11 +1571,10 @@ private: // Parse frac = decimal-point 1*DIGIT int expFrac = 0; size_t decimalPosition; - if (s.Peek() == '.') { - s.Take(); + if (Consume(s, '.')) { decimalPosition = s.Length(); - if (!(s.Peek() >= '0' && s.Peek() <= '9')) + if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); if (!useDouble) { @@ -872,8 +1582,8 @@ private: // Use i64 to store significand in 64-bit architecture if (!use64bit) i64 = i; - - while (s.Peek() >= '0' && s.Peek() <= '9') { + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path break; else { @@ -884,19 +1594,19 @@ private: } } - d = (double)i64; + d = static_cast(i64); #else // Use double to store significand in 32-bit architecture - d = use64bit ? (double)i64 : (double)i; + d = static_cast(use64bit ? i64 : i); #endif useDouble = true; } - while (s.Peek() >= '0' && s.Peek() <= '9') { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { if (significandDigit < 17) { d = d * 10.0 + (s.TakePush() - '0'); --expFrac; - if (d > 0.0) + if (RAPIDJSON_LIKELY(d > 0.0)) significandDigit++; } else @@ -908,38 +1618,35 @@ private: // Parse exp = e [ minus / plus ] 1*DIGIT int exp = 0; - if (s.Peek() == 'e' || s.Peek() == 'E') { + if (Consume(s, 'e') || Consume(s, 'E')) { if (!useDouble) { - d = use64bit ? i64 : i; + d = static_cast(use64bit ? i64 : i); useDouble = true; } - s.Take(); bool expMinus = false; - if (s.Peek() == '+') - s.Take(); - else if (s.Peek() == '-') { - s.Take(); + if (Consume(s, '+')) + ; + else if (Consume(s, '-')) expMinus = true; - } - if (s.Peek() >= '0' && s.Peek() <= '9') { - exp = s.Take() - '0'; + if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = static_cast(s.Take() - '0'); if (expMinus) { - while (s.Peek() >= '0' && s.Peek() <= '9') { - exp = exp * 10 + (s.Take() - '0'); + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); if (exp >= 214748364) { // Issue #313: prevent overflow exponent - while (s.Peek() >= '0' && s.Peek() <= '9') // Consume the rest of exponent + while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent s.Take(); } } } else { // positive exp int maxExp = 308 - expFrac; - while (s.Peek() >= '0' && s.Peek() <= '9') { - exp = exp * 10 + (s.Take() - '0'); - if (exp > maxExp) - RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (RAPIDJSON_UNLIKELY(exp > maxExp)) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); } } } @@ -952,34 +1659,63 @@ private: // Finish parsing, call event according to the type of number. bool cont = true; - size_t length = s.Length(); - const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. - if (useDouble) { - int p = exp + expFrac; - if (parseFlags & kParseFullPrecisionFlag) - d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); - else - d = internal::StrtodNormalPrecision(d, p); - - cont = handler.Double(minus ? -d : d); - } - else { - if (use64bit) { - if (minus) - cont = handler.Int64(-(int64_t)i64); - else - cont = handler.Uint64(i64); + if (parseFlags & kParseNumbersAsStringsFlag) { + if (parseFlags & kParseInsituFlag) { + s.Pop(); // Pop stack no matter if it will be used or not. + typename InputStream::Ch* head = is.PutBegin(); + const size_t length = s.Tell() - startOffset; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + // unable to insert the \0 character here, it will erase the comma after this number + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + cont = handler.RawNumber(str, SizeType(length), false); } else { - if (minus) - cont = handler.Int(-(int)i); - else - cont = handler.Uint(i); + SizeType numCharsToCopy = static_cast(s.Length()); + StringStream srcStream(s.Pop()); + StackStream dstStream(stack_); + while (numCharsToCopy--) { + Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); + } + dstStream.Put('\0'); + const typename TargetEncoding::Ch* str = dstStream.Pop(); + const SizeType length = static_cast(dstStream.Length()) - 1; + cont = handler.RawNumber(str, SizeType(length), true); } } - if (!cont) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + else { + size_t length = s.Length(); + const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. + + if (useDouble) { + int p = exp + expFrac; + if (parseFlags & kParseFullPrecisionFlag) + d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); + else + d = internal::StrtodNormalPrecision(d, p); + + cont = handler.Double(minus ? -d : d); + } + else if (useNanOrInf) { + cont = handler.Double(d); + } + else { + if (use64bit) { + if (minus) + cont = handler.Int64(static_cast(~i64 + 1)); + else + cont = handler.Uint64(i64); + } + else { + if (minus) + cont = handler.Int(static_cast(~i + 1)); + else + cont = handler.Uint(i); + } + } + } + if (RAPIDJSON_UNLIKELY(!cont)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); } // Parse any JSON value @@ -992,7 +1728,10 @@ private: case '"': ParseString(is, handler); break; case '{': ParseObject(is, handler); break; case '[': ParseArray (is, handler); break; - default : ParseNumber(is, handler); + default : + ParseNumber(is, handler); + break; + } } @@ -1000,27 +1739,29 @@ private: // States enum IterativeParsingState { - IterativeParsingStartState = 0, - IterativeParsingFinishState, - IterativeParsingErrorState, + IterativeParsingFinishState = 0, // sink states at top + IterativeParsingErrorState, // sink states at top + IterativeParsingStartState, // Object states IterativeParsingObjectInitialState, IterativeParsingMemberKeyState, - IterativeParsingKeyValueDelimiterState, IterativeParsingMemberValueState, - IterativeParsingMemberDelimiterState, IterativeParsingObjectFinishState, // Array states IterativeParsingArrayInitialState, IterativeParsingElementState, - IterativeParsingElementDelimiterState, IterativeParsingArrayFinishState, // Single value state IterativeParsingValueState, - + + // Delimiter states (at bottom) + IterativeParsingElementDelimiterState, + IterativeParsingMemberDelimiterState, + IterativeParsingKeyValueDelimiterState, + cIterativeParsingStateCount }; @@ -1064,9 +1805,9 @@ private: #undef N #undef N16 //!@endcond - - if (sizeof(Ch) == 1 || unsigned(c) < 256) - return (Token)tokenMap[(unsigned char)c]; + + if (sizeof(Ch) == 1 || static_cast(c) < 256) + return static_cast(tokenMap[static_cast(c)]); else return NumberToken; } @@ -1074,6 +1815,18 @@ private: RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) { // current state x one lookahead token -> new state static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, // Start { IterativeParsingArrayInitialState, // Left bracket @@ -1088,18 +1841,6 @@ private: IterativeParsingValueState, // Null IterativeParsingValueState // Number }, - // Finish(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, - // Error(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, // ObjectInitial { IterativeParsingErrorState, // Left bracket @@ -1128,20 +1869,6 @@ private: IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, - // KeyValueDelimiter - { - IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) - IterativeParsingErrorState, // Right bracket - IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberValueState, // String - IterativeParsingMemberValueState, // False - IterativeParsingMemberValueState, // True - IterativeParsingMemberValueState, // Null - IterativeParsingMemberValueState // Number - }, // MemberValue { IterativeParsingErrorState, // Left bracket @@ -1156,20 +1883,6 @@ private: IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, - // MemberDelimiter - { - IterativeParsingErrorState, // Left bracket - IterativeParsingErrorState, // Right bracket - IterativeParsingErrorState, // Left curly bracket - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberKeyState, // String - IterativeParsingErrorState, // False - IterativeParsingErrorState, // True - IterativeParsingErrorState, // Null - IterativeParsingErrorState // Number - }, // ObjectFinish(sink state) { IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, @@ -1204,20 +1917,6 @@ private: IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, - // ElementDelimiter - { - IterativeParsingArrayInitialState, // Left bracket(push Element state) - IterativeParsingErrorState, // Right bracket - IterativeParsingObjectInitialState, // Left curly bracket(push Element state) - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingElementState, // String - IterativeParsingElementState, // False - IterativeParsingElementState, // True - IterativeParsingElementState, // Null - IterativeParsingElementState // Number - }, // ArrayFinish(sink state) { IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, @@ -1229,10 +1928,52 @@ private: IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState - } + }, + // ElementDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // MemberDelimiter + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // KeyValueDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, }; // End of G - return (IterativeParsingState)G[state][token]; + return static_cast(G[state][token]); } // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). @@ -1309,6 +2050,11 @@ private: case IterativeParsingObjectFinishState: { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell()); + return IterativeParsingErrorState; + } // Get member count. SizeType c = *stack_.template Pop(1); // If the object is not empty, count the last member. @@ -1334,6 +2080,11 @@ private: case IterativeParsingArrayFinishState: { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell()); + return IterativeParsingErrorState; + } // Get element count. SizeType c = *stack_.template Pop(1); // If the array is not empty, count the last element. @@ -1385,55 +2136,68 @@ private: // Error flag has been set. return; } - + switch (src) { - case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); - case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); + case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return; + case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return; case IterativeParsingObjectInitialState: - case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); - case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); - case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); - case IterativeParsingElementState: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); - default: RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); - } + case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return; + case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return; + case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return; + case IterativeParsingKeyValueDelimiterState: + case IterativeParsingArrayInitialState: + case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return; + default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return; + } } + RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) { + return s >= IterativeParsingElementDelimiterState; + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) { + return s <= IterativeParsingErrorState; + } + template ParseResult IterativeParse(InputStream& is, Handler& handler) { parseResult_.Clear(); ClearStackOnExit scope(*this); IterativeParsingState state = IterativeParsingStartState; - - SkipWhitespace(is); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); while (is.Peek() != '\0') { Token t = Tokenize(is.Peek()); IterativeParsingState n = Predict(state, t); IterativeParsingState d = Transit(state, t, n, is, handler); - + if (d == IterativeParsingErrorState) { HandleError(state, is); break; } - + state = d; - + // Do not further consume streams if a root JSON has been parsed. if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) break; - - SkipWhitespace(is); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); } - + // Handle the end of file. if (state != IterativeParsingFinishState) HandleError(state, is); - + return parseResult_; } static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. ParseResult parseResult_; + IterativeParsingState state_; }; // class GenericReader //! Reader with UTF8 encoding and default allocator. @@ -1441,6 +2205,11 @@ typedef GenericReader, UTF8<> > Reader; RAPIDJSON_NAMESPACE_END +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + + #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif diff --git a/contrib/rapidjson/include/rapidjson/schema.h b/contrib/rapidjson/include/rapidjson/schema.h new file mode 100644 index 000000000..abcf1a102 --- /dev/null +++ b/contrib/rapidjson/include/rapidjson/schema.h @@ -0,0 +1,2016 @@ +// Tencent is pleased to support the open source community by making RapidJSON available-> +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License-> You may obtain a copy of the License at +// +// http://opensource->org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied-> See the License for the +// specific language governing permissions and limitations under the License-> + +#ifndef RAPIDJSON_SCHEMA_H_ +#define RAPIDJSON_SCHEMA_H_ + +#include "document.h" +#include "pointer.h" +#include // abs, floor + +#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0 +#endif + +#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) +#define RAPIDJSON_SCHEMA_USE_STDREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_STDREGEX 0 +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX +#include "internal/regex.h" +#elif RAPIDJSON_SCHEMA_USE_STDREGEX +#include +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX +#define RAPIDJSON_SCHEMA_HAS_REGEX 1 +#else +#define RAPIDJSON_SCHEMA_HAS_REGEX 0 +#endif + +#ifndef RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_VERBOSE 0 +#endif + +#if RAPIDJSON_SCHEMA_VERBOSE +#include "stringbuffer.h" +#endif + +RAPIDJSON_DIAG_PUSH + +#if defined(__GNUC__) +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(weak-vtables) +RAPIDJSON_DIAG_OFF(exit-time-destructors) +RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) +RAPIDJSON_DIAG_OFF(variadic-macros) +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Verbose Utilities + +#if RAPIDJSON_SCHEMA_VERBOSE + +namespace internal { + +inline void PrintInvalidKeyword(const char* keyword) { + printf("Fail keyword: %s\n", keyword); +} + +inline void PrintInvalidKeyword(const wchar_t* keyword) { + wprintf(L"Fail keyword: %ls\n", keyword); +} + +inline void PrintInvalidDocument(const char* document) { + printf("Fail document: %s\n\n", document); +} + +inline void PrintInvalidDocument(const wchar_t* document) { + wprintf(L"Fail document: %ls\n\n", document); +} + +inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) { + printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d); +} + +inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) { + wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d); +} + +} // namespace internal + +#endif // RAPIDJSON_SCHEMA_VERBOSE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_INVALID_KEYWORD_RETURN + +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword) +#else +#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) +#endif + +#define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + context.invalidKeyword = keyword.GetString();\ + RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\ + return false;\ +RAPIDJSON_MULTILINEMACRO_END + +/////////////////////////////////////////////////////////////////////////////// +// Forward declarations + +template +class GenericSchemaDocument; + +namespace internal { + +template +class Schema; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaValidator + +class ISchemaValidator { +public: + virtual ~ISchemaValidator() {} + virtual bool IsValid() const = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaStateFactory + +template +class ISchemaStateFactory { +public: + virtual ~ISchemaStateFactory() {} + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0; + virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; + virtual void* CreateHasher() = 0; + virtual uint64_t GetHashCode(void* hasher) = 0; + virtual void DestroryHasher(void* hasher) = 0; + virtual void* MallocState(size_t size) = 0; + virtual void FreeState(void* p) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Hasher + +// For comparison of compound value +template +class Hasher { +public: + typedef typename Encoding::Ch Ch; + + Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {} + + bool Null() { return WriteType(kNullType); } + bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); } + bool Int(int i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Double(double d) { + Number n; + if (d < 0) n.u.i = static_cast(d); + else n.u.u = static_cast(d); + n.d = d; + return WriteNumber(n); + } + + bool RawNumber(const Ch* str, SizeType len, bool) { + WriteBuffer(kNumberType, str, len * sizeof(Ch)); + return true; + } + + bool String(const Ch* str, SizeType len, bool) { + WriteBuffer(kStringType, str, len * sizeof(Ch)); + return true; + } + + bool StartObject() { return true; } + bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } + bool EndObject(SizeType memberCount) { + uint64_t h = Hash(0, kObjectType); + uint64_t* kv = stack_.template Pop(memberCount * 2); + for (SizeType i = 0; i < memberCount; i++) + h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive + *stack_.template Push() = h; + return true; + } + + bool StartArray() { return true; } + bool EndArray(SizeType elementCount) { + uint64_t h = Hash(0, kArrayType); + uint64_t* e = stack_.template Pop(elementCount); + for (SizeType i = 0; i < elementCount; i++) + h = Hash(h, e[i]); // Use hash to achieve element order sensitive + *stack_.template Push() = h; + return true; + } + + bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); } + + uint64_t GetHashCode() const { + RAPIDJSON_ASSERT(IsValid()); + return *stack_.template Top(); + } + +private: + static const size_t kDefaultSize = 256; + struct Number { + union U { + uint64_t u; + int64_t i; + }u; + double d; + }; + + bool WriteType(Type type) { return WriteBuffer(type, 0, 0); } + + bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); } + + bool WriteBuffer(Type type, const void* data, size_t len) { + // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/ + uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type); + const unsigned char* d = static_cast(data); + for (size_t i = 0; i < len; i++) + h = Hash(h, d[i]); + *stack_.template Push() = h; + return true; + } + + static uint64_t Hash(uint64_t h, uint64_t d) { + static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3); + h ^= d; + h *= kPrime; + return h; + } + + Stack stack_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidationContext + +template +struct SchemaValidationContext { + typedef Schema SchemaType; + typedef ISchemaStateFactory SchemaValidatorFactoryType; + typedef typename SchemaType::ValueType ValueType; + typedef typename ValueType::Ch Ch; + + enum PatternValidatorType { + kPatternValidatorOnly, + kPatternValidatorWithProperty, + kPatternValidatorWithAdditionalProperty + }; + + SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) : + factory(f), + schema(s), + valueSchema(), + invalidKeyword(), + hasher(), + arrayElementHashCodes(), + validators(), + validatorCount(), + patternPropertiesValidators(), + patternPropertiesValidatorCount(), + patternPropertiesSchemas(), + patternPropertiesSchemaCount(), + valuePatternValidatorType(kPatternValidatorOnly), + propertyExist(), + inArray(false), + valueUniqueness(false), + arrayUniqueness(false) + { + } + + ~SchemaValidationContext() { + if (hasher) + factory.DestroryHasher(hasher); + if (validators) { + for (SizeType i = 0; i < validatorCount; i++) + factory.DestroySchemaValidator(validators[i]); + factory.FreeState(validators); + } + if (patternPropertiesValidators) { + for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) + factory.DestroySchemaValidator(patternPropertiesValidators[i]); + factory.FreeState(patternPropertiesValidators); + } + if (patternPropertiesSchemas) + factory.FreeState(patternPropertiesSchemas); + if (propertyExist) + factory.FreeState(propertyExist); + } + + SchemaValidatorFactoryType& factory; + const SchemaType* schema; + const SchemaType* valueSchema; + const Ch* invalidKeyword; + void* hasher; // Only validator access + void* arrayElementHashCodes; // Only validator access this + ISchemaValidator** validators; + SizeType validatorCount; + ISchemaValidator** patternPropertiesValidators; + SizeType patternPropertiesValidatorCount; + const SchemaType** patternPropertiesSchemas; + SizeType patternPropertiesSchemaCount; + PatternValidatorType valuePatternValidatorType; + PatternValidatorType objectPatternValidatorType; + SizeType arrayElementIndex; + bool* propertyExist; + bool inArray; + bool valueUniqueness; + bool arrayUniqueness; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Schema + +template +class Schema { +public: + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef SchemaValidationContext Context; + typedef Schema SchemaType; + typedef GenericValue SValue; + friend class GenericSchemaDocument; + + Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : + allocator_(allocator), + typeless_(schemaDocument->GetTypeless()), + enum_(), + enumCount_(), + not_(), + type_((1 << kTotalSchemaType) - 1), // typeless + validatorCount_(), + properties_(), + additionalPropertiesSchema_(), + patternProperties_(), + patternPropertyCount_(), + propertyCount_(), + minProperties_(), + maxProperties_(SizeType(~0)), + additionalProperties_(true), + hasDependencies_(), + hasRequired_(), + hasSchemaDependencies_(), + additionalItemsSchema_(), + itemsList_(), + itemsTuple_(), + itemsTupleCount_(), + minItems_(), + maxItems_(SizeType(~0)), + additionalItems_(true), + uniqueItems_(false), + pattern_(), + minLength_(0), + maxLength_(~SizeType(0)), + exclusiveMinimum_(false), + exclusiveMaximum_(false) + { + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename ValueType::ConstValueIterator ConstValueIterator; + typedef typename ValueType::ConstMemberIterator ConstMemberIterator; + + if (!value.IsObject()) + return; + + if (const ValueType* v = GetMember(value, GetTypeString())) { + type_ = 0; + if (v->IsString()) + AddType(*v); + else if (v->IsArray()) + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) + AddType(*itr); + } + + if (const ValueType* v = GetMember(value, GetEnumString())) + if (v->IsArray() && v->Size() > 0) { + enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { + typedef Hasher > EnumHasherType; + char buffer[256 + 24]; + MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer)); + EnumHasherType h(&hasherAllocator, 256); + itr->Accept(h); + enum_[enumCount_++] = h.GetHashCode(); + } + } + + if (schemaDocument) { + AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); + AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); + AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); + } + + if (const ValueType* v = GetMember(value, GetNotString())) { + schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document); + notValidatorIndex_ = validatorCount_; + validatorCount_++; + } + + // Object + + const ValueType* properties = GetMember(value, GetPropertiesString()); + const ValueType* required = GetMember(value, GetRequiredString()); + const ValueType* dependencies = GetMember(value, GetDependenciesString()); + { + // Gather properties from properties/required/dependencies + SValue allProperties(kArrayType); + + if (properties && properties->IsObject()) + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) + AddUniqueElement(allProperties, itr->name); + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) + AddUniqueElement(allProperties, *itr); + + if (dependencies && dependencies->IsObject()) + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + AddUniqueElement(allProperties, itr->name); + if (itr->value.IsArray()) + for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i) + if (i->IsString()) + AddUniqueElement(allProperties, *i); + } + + if (allProperties.Size() > 0) { + propertyCount_ = allProperties.Size(); + properties_ = static_cast(allocator_->Malloc(sizeof(Property) * propertyCount_)); + for (SizeType i = 0; i < propertyCount_; i++) { + new (&properties_[i]) Property(); + properties_[i].name = allProperties[i]; + properties_[i].schema = typeless_; + } + } + } + + if (properties && properties->IsObject()) { + PointerType q = p.Append(GetPropertiesString(), allocator_); + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { + SizeType index; + if (FindPropertyIndex(itr->name, &index)) + schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document); + } + } + + if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { + PointerType q = p.Append(GetPatternPropertiesString(), allocator_); + patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); + patternPropertyCount_ = 0; + + for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { + new (&patternProperties_[patternPropertyCount_]) PatternProperty(); + patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name); + schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document); + patternPropertyCount_++; + } + } + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) { + SizeType index; + if (FindPropertyIndex(*itr, &index)) { + properties_[index].required = true; + hasRequired_ = true; + } + } + + if (dependencies && dependencies->IsObject()) { + PointerType q = p.Append(GetDependenciesString(), allocator_); + hasDependencies_ = true; + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + SizeType sourceIndex; + if (FindPropertyIndex(itr->name, &sourceIndex)) { + if (itr->value.IsArray()) { + properties_[sourceIndex].dependencies = static_cast(allocator_->Malloc(sizeof(bool) * propertyCount_)); + std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_); + for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) { + SizeType targetIndex; + if (FindPropertyIndex(*targetItr, &targetIndex)) + properties_[sourceIndex].dependencies[targetIndex] = true; + } + } + else if (itr->value.IsObject()) { + hasSchemaDependencies_ = true; + schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document); + properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; + validatorCount_++; + } + } + } + } + + if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) { + if (v->IsBool()) + additionalProperties_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document); + } + + AssignIfExist(minProperties_, value, GetMinPropertiesString()); + AssignIfExist(maxProperties_, value, GetMaxPropertiesString()); + + // Array + if (const ValueType* v = GetMember(value, GetItemsString())) { + PointerType q = p.Append(GetItemsString(), allocator_); + if (v->IsObject()) // List validation + schemaDocument->CreateSchema(&itemsList_, q, *v, document); + else if (v->IsArray()) { // Tuple validation + itemsTuple_ = static_cast(allocator_->Malloc(sizeof(const Schema*) * v->Size())); + SizeType index = 0; + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++) + schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document); + } + } + + AssignIfExist(minItems_, value, GetMinItemsString()); + AssignIfExist(maxItems_, value, GetMaxItemsString()); + + if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { + if (v->IsBool()) + additionalItems_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document); + } + + AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); + + // String + AssignIfExist(minLength_, value, GetMinLengthString()); + AssignIfExist(maxLength_, value, GetMaxLengthString()); + + if (const ValueType* v = GetMember(value, GetPatternString())) + pattern_ = CreatePattern(*v); + + // Number + if (const ValueType* v = GetMember(value, GetMinimumString())) + if (v->IsNumber()) + minimum_.CopyFrom(*v, *allocator_); + + if (const ValueType* v = GetMember(value, GetMaximumString())) + if (v->IsNumber()) + maximum_.CopyFrom(*v, *allocator_); + + AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString()); + AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString()); + + if (const ValueType* v = GetMember(value, GetMultipleOfString())) + if (v->IsNumber() && v->GetDouble() > 0.0) + multipleOf_.CopyFrom(*v, *allocator_); + } + + ~Schema() { + AllocatorType::Free(enum_); + if (properties_) { + for (SizeType i = 0; i < propertyCount_; i++) + properties_[i].~Property(); + AllocatorType::Free(properties_); + } + if (patternProperties_) { + for (SizeType i = 0; i < patternPropertyCount_; i++) + patternProperties_[i].~PatternProperty(); + AllocatorType::Free(patternProperties_); + } + AllocatorType::Free(itemsTuple_); +#if RAPIDJSON_SCHEMA_HAS_REGEX + if (pattern_) { + pattern_->~RegexType(); + AllocatorType::Free(pattern_); + } +#endif + } + + bool BeginValue(Context& context) const { + if (context.inArray) { + if (uniqueItems_) + context.valueUniqueness = true; + + if (itemsList_) + context.valueSchema = itemsList_; + else if (itemsTuple_) { + if (context.arrayElementIndex < itemsTupleCount_) + context.valueSchema = itemsTuple_[context.arrayElementIndex]; + else if (additionalItemsSchema_) + context.valueSchema = additionalItemsSchema_; + else if (additionalItems_) + context.valueSchema = typeless_; + else + RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString()); + } + else + context.valueSchema = typeless_; + + context.arrayElementIndex++; + } + return true; + } + + RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { + if (context.patternPropertiesValidatorCount > 0) { + bool otherValid = false; + SizeType count = context.patternPropertiesValidatorCount; + if (context.objectPatternValidatorType != Context::kPatternValidatorOnly) + otherValid = context.patternPropertiesValidators[--count]->IsValid(); + + bool patternValid = true; + for (SizeType i = 0; i < count; i++) + if (!context.patternPropertiesValidators[i]->IsValid()) { + patternValid = false; + break; + } + + if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { + if (!patternValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { + if (!patternValid || !otherValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + + if (enum_) { + const uint64_t h = context.factory.GetHashCode(context.hasher); + for (SizeType i = 0; i < enumCount_; i++) + if (enum_[i] == h) + goto foundEnum; + RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString()); + foundEnum:; + } + + if (allOf_.schemas) + for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) + if (!context.validators[i]->IsValid()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString()); + + if (anyOf_.schemas) { + for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) + if (context.validators[i]->IsValid()) + goto foundAny; + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString()); + foundAny:; + } + + if (oneOf_.schemas) { + bool oneValid = false; + for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) + if (context.validators[i]->IsValid()) { + if (oneValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); + else + oneValid = true; + } + if (!oneValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); + } + + if (not_ && context.validators[notValidatorIndex_]->IsValid()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString()); + + return true; + } + + bool Null(Context& context) const { + if (!(type_ & (1 << kNullSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + return CreateParallelValidator(context); + } + + bool Bool(Context& context, bool) const { + if (!(type_ & (1 << kBooleanSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + return CreateParallelValidator(context); + } + + bool Int(Context& context, int i) const { + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint(Context& context, unsigned u) const { + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Int64(Context& context, int64_t i) const { + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint64(Context& context, uint64_t u) const { + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Double(Context& context, double d) const { + if (!(type_ & (1 << kNumberSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) + return false; + + if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) + return false; + + if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) + return false; + + return CreateParallelValidator(context); + } + + bool String(Context& context, const Ch* str, SizeType length, bool) const { + if (!(type_ & (1 << kStringSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (minLength_ != 0 || maxLength_ != SizeType(~0)) { + SizeType count; + if (internal::CountStringCodePoint(str, length, &count)) { + if (count < minLength_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString()); + if (count > maxLength_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString()); + } + } + + if (pattern_ && !IsPatternMatch(pattern_, str, length)) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString()); + + return CreateParallelValidator(context); + } + + bool StartObject(Context& context) const { + if (!(type_ & (1 << kObjectSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (hasDependencies_ || hasRequired_) { + context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); + std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_); + } + + if (patternProperties_) { // pre-allocate schema array + SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType + context.patternPropertiesSchemas = static_cast(context.factory.MallocState(sizeof(const SchemaType*) * count)); + context.patternPropertiesSchemaCount = 0; + std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count); + } + + return CreateParallelValidator(context); + } + + bool Key(Context& context, const Ch* str, SizeType len, bool) const { + if (patternProperties_) { + context.patternPropertiesSchemaCount = 0; + for (SizeType i = 0; i < patternPropertyCount_; i++) + if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; + context.valueSchema = typeless_; + } + } + + SizeType index; + if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { + if (context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; + context.valueSchema = typeless_; + context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; + } + else + context.valueSchema = properties_[index].schema; + + if (context.propertyExist) + context.propertyExist[index] = true; + + return true; + } + + if (additionalPropertiesSchema_) { + if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; + context.valueSchema = typeless_; + context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; + } + else + context.valueSchema = additionalPropertiesSchema_; + return true; + } + else if (additionalProperties_) { + context.valueSchema = typeless_; + return true; + } + + if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString()); + + return true; + } + + bool EndObject(Context& context, SizeType memberCount) const { + if (hasRequired_) + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].required) + if (!context.propertyExist[index]) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); + + if (memberCount < minProperties_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); + + if (memberCount > maxProperties_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString()); + + if (hasDependencies_) { + for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) + if (context.propertyExist[sourceIndex]) { + if (properties_[sourceIndex].dependencies) { + for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) + if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex]) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); + } + else if (properties_[sourceIndex].dependenciesSchema) + if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); + } + } + + return true; + } + + bool StartArray(Context& context) const { + if (!(type_ & (1 << kArraySchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + context.arrayElementIndex = 0; + context.inArray = true; + + return CreateParallelValidator(context); + } + + bool EndArray(Context& context, SizeType elementCount) const { + context.inArray = false; + + if (elementCount < minItems_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString()); + + if (elementCount > maxItems_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString()); + + return true; + } + + // Generate functions for string literal according to Ch +#define RAPIDJSON_STRING_(name, ...) \ + static const ValueType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const ValueType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1));\ + return v;\ + } + + RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') + RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n') + RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't') + RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y') + RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g') + RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r') + RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r') + RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e') + RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm') + RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f') + RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f') + RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f') + RAPIDJSON_STRING_(Not, 'n', 'o', 't') + RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd') + RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's') + RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n') + RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') + +#undef RAPIDJSON_STRING_ + +private: + enum SchemaValueType { + kNullSchemaType, + kBooleanSchemaType, + kObjectSchemaType, + kArraySchemaType, + kStringSchemaType, + kNumberSchemaType, + kIntegerSchemaType, + kTotalSchemaType + }; + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + typedef internal::GenericRegex RegexType; +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + typedef std::basic_regex RegexType; +#else + typedef char RegexType; +#endif + + struct SchemaArray { + SchemaArray() : schemas(), count() {} + ~SchemaArray() { AllocatorType::Free(schemas); } + const SchemaType** schemas; + SizeType begin; // begin index of context.validators + SizeType count; + }; + + template + void AddUniqueElement(V1& a, const V2& v) { + for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) + if (*itr == v) + return; + V1 c(v, *allocator_); + a.PushBack(c, *allocator_); + } + + static const ValueType* GetMember(const ValueType& value, const ValueType& name) { + typename ValueType::ConstMemberIterator itr = value.FindMember(name); + return itr != value.MemberEnd() ? &(itr->value) : 0; + } + + static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsBool()) + out = v->GetBool(); + } + + static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsUint64() && v->GetUint64() <= SizeType(~0)) + out = static_cast(v->GetUint64()); + } + + void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) { + if (const ValueType* v = GetMember(value, name)) { + if (v->IsArray() && v->Size() > 0) { + PointerType q = p.Append(name, allocator_); + out.count = v->Size(); + out.schemas = static_cast(allocator_->Malloc(out.count * sizeof(const Schema*))); + memset(out.schemas, 0, sizeof(Schema*)* out.count); + for (SizeType i = 0; i < out.count; i++) + schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document); + out.begin = validatorCount_; + validatorCount_ += out.count; + } + } + } + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) { + RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString()); + if (!r->IsValid()) { + r->~RegexType(); + AllocatorType::Free(r); + r = 0; + } + return r; + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { + GenericRegexSearch rs(*pattern); + return rs.Search(str); + } +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) + try { + return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); + } + catch (const std::regex_error&) { + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) { + std::match_results r; + return std::regex_search(str, str + length, r, *pattern); + } +#else + template + RegexType* CreatePattern(const ValueType&) { return 0; } + + static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } +#endif // RAPIDJSON_SCHEMA_USE_STDREGEX + + void AddType(const ValueType& type) { + if (type == GetNullString() ) type_ |= 1 << kNullSchemaType; + else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType; + else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType; + else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType; + else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType; + else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType; + else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); + } + + bool CreateParallelValidator(Context& context) const { + if (enum_ || context.arrayUniqueness) + context.hasher = context.factory.CreateHasher(); + + if (validatorCount_) { + RAPIDJSON_ASSERT(context.validators == 0); + context.validators = static_cast(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); + context.validatorCount = validatorCount_; + + if (allOf_.schemas) + CreateSchemaValidators(context, allOf_); + + if (anyOf_.schemas) + CreateSchemaValidators(context, anyOf_); + + if (oneOf_.schemas) + CreateSchemaValidators(context, oneOf_); + + if (not_) + context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_); + + if (hasSchemaDependencies_) { + for (SizeType i = 0; i < propertyCount_; i++) + if (properties_[i].dependenciesSchema) + context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema); + } + } + + return true; + } + + void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const { + for (SizeType i = 0; i < schemas.count; i++) + context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]); + } + + // O(n) + bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const { + SizeType len = name.GetStringLength(); + const Ch* str = name.GetString(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].name.GetStringLength() == len && + (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) + { + *outIndex = index; + return true; + } + return false; + } + + bool CheckInt(Context& context, int64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull()) { + if (minimum_.IsInt64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } + else if (minimum_.IsUint64()) { + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64() + } + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsInt64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } + else if (maximum_.IsUint64()) { } + /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckUint(Context& context, uint64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull()) { + if (minimum_.IsUint64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } + else if (minimum_.IsInt64()) + /* do nothing */; // i >= 0 > minimum.Getint64() + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsUint64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } + else if (maximum_.IsInt64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_ + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (i % multipleOf_.GetUint64() != 0) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckDoubleMinimum(Context& context, double d) const { + if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + return true; + } + + bool CheckDoubleMaximum(Context& context, double d) const { + if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + return true; + } + + bool CheckDoubleMultipleOf(Context& context, double d) const { + double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); + double q = std::floor(a / b); + double r = a - q * b; + if (r > 0.0) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + return true; + } + + struct Property { + Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} + ~Property() { AllocatorType::Free(dependencies); } + SValue name; + const SchemaType* schema; + const SchemaType* dependenciesSchema; + SizeType dependenciesValidatorIndex; + bool* dependencies; + bool required; + }; + + struct PatternProperty { + PatternProperty() : schema(), pattern() {} + ~PatternProperty() { + if (pattern) { + pattern->~RegexType(); + AllocatorType::Free(pattern); + } + } + const SchemaType* schema; + RegexType* pattern; + }; + + AllocatorType* allocator_; + const SchemaType* typeless_; + uint64_t* enum_; + SizeType enumCount_; + SchemaArray allOf_; + SchemaArray anyOf_; + SchemaArray oneOf_; + const SchemaType* not_; + unsigned type_; // bitmask of kSchemaType + SizeType validatorCount_; + SizeType notValidatorIndex_; + + Property* properties_; + const SchemaType* additionalPropertiesSchema_; + PatternProperty* patternProperties_; + SizeType patternPropertyCount_; + SizeType propertyCount_; + SizeType minProperties_; + SizeType maxProperties_; + bool additionalProperties_; + bool hasDependencies_; + bool hasRequired_; + bool hasSchemaDependencies_; + + const SchemaType* additionalItemsSchema_; + const SchemaType* itemsList_; + const SchemaType** itemsTuple_; + SizeType itemsTupleCount_; + SizeType minItems_; + SizeType maxItems_; + bool additionalItems_; + bool uniqueItems_; + + RegexType* pattern_; + SizeType minLength_; + SizeType maxLength_; + + SValue minimum_; + SValue maximum_; + SValue multipleOf_; + bool exclusiveMinimum_; + bool exclusiveMaximum_; +}; + +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + *documentStack.template Push() = '/'; + char buffer[21]; + size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); + for (size_t i = 0; i < length; i++) + *documentStack.template Push() = static_cast(buffer[i]); + } +}; + +// Partial specialized version for char to prevent buffer copying. +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + if (sizeof(SizeType) == 4) { + char *buffer = documentStack.template Push(1 + 10); // '/' + uint + *buffer++ = '/'; + const char* end = internal::u32toa(index, buffer); + documentStack.template Pop(static_cast(10 - (end - buffer))); + } + else { + char *buffer = documentStack.template Push(1 + 20); // '/' + uint64 + *buffer++ = '/'; + const char* end = internal::u64toa(index, buffer); + documentStack.template Pop(static_cast(20 - (end - buffer))); + } + } +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// IGenericRemoteSchemaDocumentProvider + +template +class IGenericRemoteSchemaDocumentProvider { +public: + typedef typename SchemaDocumentType::Ch Ch; + + virtual ~IGenericRemoteSchemaDocumentProvider() {} + virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaDocument + +//! JSON schema document. +/*! + A JSON schema document is a compiled version of a JSON schema. + It is basically a tree of internal::Schema. + + \note This is an immutable class (i.e. its instance cannot be modified after construction). + \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding. + \tparam Allocator Allocator type for allocating memory of this document. +*/ +template +class GenericSchemaDocument { +public: + typedef ValueT ValueType; + typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProviderType; + typedef Allocator AllocatorType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef internal::Schema SchemaType; + typedef GenericPointer PointerType; + friend class internal::Schema; + template + friend class GenericSchemaValidator; + + //! Constructor. + /*! + Compile a JSON document into schema document. + + \param document A JSON document as source. + \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. + \param allocator An optional allocator instance for allocating memory. Can be null. + */ + explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : + remoteProvider_(remoteProvider), + allocator_(allocator), + ownAllocator_(), + root_(), + typeless_(), + schemaMap_(allocator, kInitialSchemaMapSize), + schemaRef_(allocator, kInitialSchemaRefSize) + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); + new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0); + + // Generate root schema, it will call CreateSchema() to create sub-schemas, + // And call AddRefSchema() if there are $ref. + CreateSchemaRecursive(&root_, PointerType(), document, document); + + // Resolve $ref + while (!schemaRef_.Empty()) { + SchemaRefEntry* refEntry = schemaRef_.template Pop(1); + if (const SchemaType* s = GetSchema(refEntry->target)) { + if (refEntry->schema) + *refEntry->schema = s; + + // Create entry in map if not exist + if (!GetSchema(refEntry->source)) { + new (schemaMap_.template Push()) SchemaEntry(refEntry->source, const_cast(s), false, allocator_); + } + } + else if (refEntry->schema) + *refEntry->schema = typeless_; + + refEntry->~SchemaRefEntry(); + } + + RAPIDJSON_ASSERT(root_ != 0); + + schemaRef_.ShrinkToFit(); // Deallocate all memory for ref + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT : + remoteProvider_(rhs.remoteProvider_), + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + root_(rhs.root_), + typeless_(rhs.typeless_), + schemaMap_(std::move(rhs.schemaMap_)), + schemaRef_(std::move(rhs.schemaRef_)) + { + rhs.remoteProvider_ = 0; + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.typeless_ = 0; + } +#endif + + //! Destructor + ~GenericSchemaDocument() { + while (!schemaMap_.Empty()) + schemaMap_.template Pop(1)->~SchemaEntry(); + + if (typeless_) { + typeless_->~SchemaType(); + Allocator::Free(typeless_); + } + + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Get the root schema. + const SchemaType& GetRoot() const { return *root_; } + +private: + //! Prohibit copying + GenericSchemaDocument(const GenericSchemaDocument&); + //! Prohibit assignment + GenericSchemaDocument& operator=(const GenericSchemaDocument&); + + struct SchemaRefEntry { + SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {} + PointerType source; + PointerType target; + const SchemaType** schema; + }; + + struct SchemaEntry { + SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} + ~SchemaEntry() { + if (owned) { + schema->~SchemaType(); + Allocator::Free(schema); + } + } + PointerType pointer; + SchemaType* schema; + bool owned; + }; + + void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + if (schema) + *schema = typeless_; + + if (v.GetType() == kObjectType) { + const SchemaType* s = GetSchema(pointer); + if (!s) + CreateSchema(schema, pointer, v, document); + + for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) + CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document); + } + else if (v.GetType() == kArrayType) + for (SizeType i = 0; i < v.Size(); i++) + CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document); + } + + void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + RAPIDJSON_ASSERT(pointer.IsValid()); + if (v.IsObject()) { + if (!HandleRefSchema(pointer, schema, v, document)) { + SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_); + new (schemaMap_.template Push()) SchemaEntry(pointer, s, true, allocator_); + if (schema) + *schema = s; + } + } + } + + bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) { + static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' }; + static const ValueType kRefValue(kRefString, 4); + + typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue); + if (itr == v.MemberEnd()) + return false; + + if (itr->value.IsString()) { + SizeType len = itr->value.GetStringLength(); + if (len > 0) { + const Ch* s = itr->value.GetString(); + SizeType i = 0; + while (i < len && s[i] != '#') // Find the first # + i++; + + if (i > 0) { // Remote reference, resolve immediately + if (remoteProvider_) { + if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) { + PointerType pointer(&s[i], len - i, allocator_); + if (pointer.IsValid()) { + if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) { + if (schema) + *schema = sc; + return true; + } + } + } + } + } + else if (s[i] == '#') { // Local reference, defer resolution + PointerType pointer(&s[i], len - i, allocator_); + if (pointer.IsValid()) { + if (const ValueType* nv = pointer.Get(document)) + if (HandleRefSchema(source, schema, *nv, document)) + return true; + + new (schemaRef_.template Push()) SchemaRefEntry(source, pointer, schema, allocator_); + return true; + } + } + } + } + return false; + } + + const SchemaType* GetSchema(const PointerType& pointer) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (pointer == target->pointer) + return target->schema; + return 0; + } + + PointerType GetPointer(const SchemaType* schema) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (schema == target->schema) + return target->pointer; + return PointerType(); + } + + const SchemaType* GetTypeless() const { return typeless_; } + + static const size_t kInitialSchemaMapSize = 64; + static const size_t kInitialSchemaRefSize = 64; + + IRemoteSchemaDocumentProviderType* remoteProvider_; + Allocator *allocator_; + Allocator *ownAllocator_; + const SchemaType* root_; //!< Root schema. + SchemaType* typeless_; + internal::Stack schemaMap_; // Stores created Pointer -> Schemas + internal::Stack schemaRef_; // Stores Pointer from $ref and schema which holds the $ref +}; + +//! GenericSchemaDocument using Value type. +typedef GenericSchemaDocument SchemaDocument; +//! IGenericRemoteSchemaDocumentProvider using SchemaDocument. +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaValidator + +//! JSON Schema Validator. +/*! + A SAX style JSON schema validator. + It uses a \c GenericSchemaDocument to validate SAX events. + It delegates the incoming SAX events to an output handler. + The default output handler does nothing. + It can be reused multiple times by calling \c Reset(). + + \tparam SchemaDocumentType Type of schema document. + \tparam OutputHandler Type of output handler. Default handler does nothing. + \tparam StateAllocator Allocator for storing the internal validation states. +*/ +template < + typename SchemaDocumentType, + typename OutputHandler = BaseReaderHandler, + typename StateAllocator = CrtAllocator> +class GenericSchemaValidator : + public internal::ISchemaStateFactory, + public internal::ISchemaValidator +{ +public: + typedef typename SchemaDocumentType::SchemaType SchemaType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename SchemaType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + + //! Constructor without output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + valid_(true) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(0) +#endif + { + } + + //! Constructor with output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + OutputHandler& outputHandler, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(&outputHandler), + valid_(true) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(0) +#endif + { + } + + //! Destructor. + ~GenericSchemaValidator() { + Reset(); + RAPIDJSON_DELETE(ownStateAllocator_); + } + + //! Reset the internal states. + void Reset() { + while (!schemaStack_.Empty()) + PopSchema(); + documentStack_.Clear(); + valid_ = true; + } + + //! Checks whether the current state is valid. + // Implementation of ISchemaValidator + virtual bool IsValid() const { return valid_; } + + //! Gets the JSON pointer pointed to the invalid schema. + PointerType GetInvalidSchemaPointer() const { + return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema()); + } + + //! Gets the keyword of invalid schema. + const Ch* GetInvalidSchemaKeyword() const { + return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword; + } + + //! Gets the JSON pointer pointed to the invalid value. + PointerType GetInvalidDocumentPointer() const { + return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + } + +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + *documentStack_.template Push() = '\0';\ + documentStack_.template Pop(1);\ + internal::PrintInvalidDocument(documentStack_.template Bottom());\ +RAPIDJSON_MULTILINEMACRO_END +#else +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() +#endif + +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ + if (!valid_) return false; \ + if (!BeginValue() || !CurrentSchema().method arg1) {\ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\ + return valid_ = false;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ + for (Context* context = schemaStack_.template Bottom(); context != schemaStack_.template End(); context++) {\ + if (context->hasher)\ + static_cast(context->hasher)->method arg2;\ + if (context->validators)\ + for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\ + static_cast(context->validators[i_])->method arg2;\ + if (context->patternPropertiesValidators)\ + for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\ + static_cast(context->patternPropertiesValidators[i_])->method arg2;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ + return valid_ = EndValue() && (!outputHandler_ || outputHandler_->method arg2) + +#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ + RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) + + bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); } + bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } + bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } + bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } + bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } + bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } + bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } + bool RawNumber(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + bool String(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + + bool StartObject() { + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); + return valid_ = !outputHandler_ || outputHandler_->StartObject(); + } + + bool Key(const Ch* str, SizeType len, bool copy) { + if (!valid_) return false; + AppendToken(str, len); + if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); + return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); + } + + bool EndObject(SizeType memberCount) { + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); + if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); + } + + bool StartArray() { + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); + return valid_ = !outputHandler_ || outputHandler_->StartArray(); + } + + bool EndArray(SizeType elementCount) { + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); + if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); + } + +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_ +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ +#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ +#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ + + // Implementation of ISchemaStateFactory + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) { + return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, +#if RAPIDJSON_SCHEMA_VERBOSE + depth_ + 1, +#endif + &GetStateAllocator()); + } + + virtual void DestroySchemaValidator(ISchemaValidator* validator) { + GenericSchemaValidator* v = static_cast(validator); + v->~GenericSchemaValidator(); + StateAllocator::Free(v); + } + + virtual void* CreateHasher() { + return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator()); + } + + virtual uint64_t GetHashCode(void* hasher) { + return static_cast(hasher)->GetHashCode(); + } + + virtual void DestroryHasher(void* hasher) { + HasherType* h = static_cast(hasher); + h->~HasherType(); + StateAllocator::Free(h); + } + + virtual void* MallocState(size_t size) { + return GetStateAllocator().Malloc(size); + } + + virtual void FreeState(void* p) { + StateAllocator::Free(p); + } + +private: + typedef typename SchemaType::Context Context; + typedef GenericValue, StateAllocator> HashCodeArray; + typedef internal::Hasher HasherType; + + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + const SchemaType& root, +#if RAPIDJSON_SCHEMA_VERBOSE + unsigned depth, +#endif + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(root), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + valid_(true) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(depth) +#endif + { + } + + StateAllocator& GetStateAllocator() { + if (!stateAllocator_) + stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)(); + return *stateAllocator_; + } + + bool BeginValue() { + if (schemaStack_.Empty()) + PushSchema(root_); + else { + if (CurrentContext().inArray) + internal::TokenHelper, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); + + if (!CurrentSchema().BeginValue(CurrentContext())) + return false; + + SizeType count = CurrentContext().patternPropertiesSchemaCount; + const SchemaType** sa = CurrentContext().patternPropertiesSchemas; + typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; + bool valueUniqueness = CurrentContext().valueUniqueness; + RAPIDJSON_ASSERT(CurrentContext().valueSchema); + PushSchema(*CurrentContext().valueSchema); + + if (count > 0) { + CurrentContext().objectPatternValidatorType = patternValidatorType; + ISchemaValidator**& va = CurrentContext().patternPropertiesValidators; + SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount; + va = static_cast(MallocState(sizeof(ISchemaValidator*) * count)); + for (SizeType i = 0; i < count; i++) + va[validatorCount++] = CreateSchemaValidator(*sa[i]); + } + + CurrentContext().arrayUniqueness = valueUniqueness; + } + return true; + } + + bool EndValue() { + if (!CurrentSchema().EndValue(CurrentContext())) + return false; + +#if RAPIDJSON_SCHEMA_VERBOSE + GenericStringBuffer sb; + schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb); + + *documentStack_.template Push() = '\0'; + documentStack_.template Pop(1); + internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom()); +#endif + + uint64_t h = CurrentContext().arrayUniqueness ? static_cast(CurrentContext().hasher)->GetHashCode() : 0; + + PopSchema(); + + if (!schemaStack_.Empty()) { + Context& context = CurrentContext(); + if (context.valueUniqueness) { + HashCodeArray* a = static_cast(context.arrayElementHashCodes); + if (!a) + CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); + for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) + if (itr->GetUint64() == h) + RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString()); + a->PushBack(h, GetStateAllocator()); + } + } + + // Remove the last token of document pointer + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') + ; + + return true; + } + + void AppendToken(const Ch* str, SizeType len) { + documentStack_.template Reserve(1 + len * 2); // worst case all characters are escaped as two characters + *documentStack_.template PushUnsafe() = '/'; + for (SizeType i = 0; i < len; i++) { + if (str[i] == '~') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '0'; + } + else if (str[i] == '/') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '1'; + } + else + *documentStack_.template PushUnsafe() = str[i]; + } + } + + RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, &schema); } + + RAPIDJSON_FORCEINLINE void PopSchema() { + Context* c = schemaStack_.template Pop(1); + if (HashCodeArray* a = static_cast(c->arrayElementHashCodes)) { + a->~HashCodeArray(); + StateAllocator::Free(a); + } + c->~Context(); + } + + const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } + Context& CurrentContext() { return *schemaStack_.template Top(); } + const Context& CurrentContext() const { return *schemaStack_.template Top(); } + + static const size_t kDefaultSchemaStackCapacity = 1024; + static const size_t kDefaultDocumentStackCapacity = 256; + const SchemaDocumentType* schemaDocument_; + const SchemaType& root_; + StateAllocator* stateAllocator_; + StateAllocator* ownStateAllocator_; + internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) + internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) + OutputHandler* outputHandler_; + bool valid_; +#if RAPIDJSON_SCHEMA_VERBOSE + unsigned depth_; +#endif +}; + +typedef GenericSchemaValidator SchemaValidator; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidatingReader + +//! A helper class for parsing with validation. +/*! + This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate(). + + \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam SourceEncoding Encoding of the input stream. + \tparam SchemaDocumentType Type of schema document. + \tparam StackAllocator Allocator type for stack. +*/ +template < + unsigned parseFlags, + typename InputStream, + typename SourceEncoding, + typename SchemaDocumentType = SchemaDocument, + typename StackAllocator = CrtAllocator> +class SchemaValidatingReader { +public: + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename InputStream::Ch Ch; + + //! Constructor + /*! + \param is Input stream. + \param sd Schema document. + */ + SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {} + + template + bool operator()(Handler& handler) { + GenericReader reader; + GenericSchemaValidator validator(sd_, handler); + parseResult_ = reader.template Parse(is_, validator); + + isValid_ = validator.IsValid(); + if (isValid_) { + invalidSchemaPointer_ = PointerType(); + invalidSchemaKeyword_ = 0; + invalidDocumentPointer_ = PointerType(); + } + else { + invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); + invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); + invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); + } + + return parseResult_; + } + + const ParseResult& GetParseResult() const { return parseResult_; } + bool IsValid() const { return isValid_; } + const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } + const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } + const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } + +private: + InputStream& is_; + const SchemaDocumentType& sd_; + + ParseResult parseResult_; + PointerType invalidSchemaPointer_; + const Ch* invalidSchemaKeyword_; + PointerType invalidDocumentPointer_; + bool isValid_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#endif // RAPIDJSON_SCHEMA_H_ diff --git a/contrib/rapidjson/include/rapidjson/stream.h b/contrib/rapidjson/include/rapidjson/stream.h new file mode 100644 index 000000000..fef82c252 --- /dev/null +++ b/contrib/rapidjson/include/rapidjson/stream.h @@ -0,0 +1,179 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#include "rapidjson.h" + +#ifndef RAPIDJSON_STREAM_H_ +#define RAPIDJSON_STREAM_H_ + +#include "encodings.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Stream + +/*! \class rapidjson::Stream + \brief Concept for reading and writing characters. + + For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). + + For write-only stream, only need to implement Put() and Flush(). + +\code +concept Stream { + typename Ch; //!< Character type of the stream. + + //! Read the current character from stream without moving the read cursor. + Ch Peek() const; + + //! Read the current character from stream and moving the read cursor to next character. + Ch Take(); + + //! Get the current read cursor. + //! \return Number of characters read from start. + size_t Tell(); + + //! Begin writing operation at the current read pointer. + //! \return The begin writer pointer. + Ch* PutBegin(); + + //! Write a character. + void Put(Ch c); + + //! Flush the buffer. + void Flush(); + + //! End the writing operation. + //! \param begin The begin write pointer returned by PutBegin(). + //! \return Number of characters written. + size_t PutEnd(Ch* begin); +} +\endcode +*/ + +//! Provides additional information for stream. +/*! + By using traits pattern, this type provides a default configuration for stream. + For custom stream, this type can be specialized for other configuration. + See TEST(Reader, CustomStringStream) in readertest.cpp for example. +*/ +template +struct StreamTraits { + //! Whether to make local copy of stream for optimization during parsing. + /*! + By default, for safety, streams do not use local copy optimization. + Stream that can be copied fast should specialize this, like StreamTraits. + */ + enum { copyOptimization = 0 }; +}; + +//! Reserve n characters for writing to a stream. +template +inline void PutReserve(Stream& stream, size_t count) { + (void)stream; + (void)count; +} + +//! Write character to a stream, presuming buffer is reserved. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c) { + stream.Put(c); +} + +//! Put N copies of a character to a stream. +template +inline void PutN(Stream& stream, Ch c, size_t n) { + PutReserve(stream, n); + for (size_t i = 0; i < n; i++) + PutUnsafe(stream, c); +} + +/////////////////////////////////////////////////////////////////////////////// +// StringStream + +//! Read-only string stream. +/*! \note implements Stream concept +*/ +template +struct GenericStringStream { + typedef typename Encoding::Ch Ch; + + GenericStringStream(const Ch *src) : src_(src), head_(src) {} + + Ch Peek() const { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() const { return static_cast(src_ - head_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! String stream with UTF8 encoding. +typedef GenericStringStream > StringStream; + +/////////////////////////////////////////////////////////////////////////////// +// InsituStringStream + +//! A read-write string stream. +/*! This string stream is particularly designed for in-situ parsing. + \note implements Stream concept +*/ +template +struct GenericInsituStringStream { + typedef typename Encoding::Ch Ch; + + GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} + + // Read + Ch Peek() { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() { return static_cast(src_ - head_); } + + // Write + void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } + + Ch* PutBegin() { return dst_ = src_; } + size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } + void Flush() {} + + Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } + void Pop(size_t count) { dst_ -= count; } + + Ch* src_; + Ch* dst_; + Ch* head_; +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! Insitu string stream with UTF8 encoding. +typedef GenericInsituStringStream > InsituStringStream; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STREAM_H_ diff --git a/contrib/rapidjson/include/rapidjson/stringbuffer.h b/contrib/rapidjson/include/rapidjson/stringbuffer.h index 1c9c80b79..4e38b82c3 100644 --- a/contrib/rapidjson/include/rapidjson/stringbuffer.h +++ b/contrib/rapidjson/include/rapidjson/stringbuffer.h @@ -15,7 +15,8 @@ #ifndef RAPIDJSON_STRINGBUFFER_H_ #define RAPIDJSON_STRINGBUFFER_H_ -#include "rapidjson.h" +#include "stream.h" +#include "internal/stack.h" #if RAPIDJSON_HAS_CXX11_RVALUE_REFS #include // std::move @@ -23,6 +24,11 @@ #include "internal/stack.h" +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + RAPIDJSON_NAMESPACE_BEGIN //! Represents an in-memory output stream. @@ -48,6 +54,7 @@ public: #endif void Put(Ch c) { *stack_.template Push() = c; } + void PutUnsafe(Ch c) { *stack_.template PushUnsafe() = c; } void Flush() {} void Clear() { stack_.Clear(); } @@ -57,7 +64,10 @@ public: stack_.ShrinkToFit(); stack_.template Pop(1); } + + void Reserve(size_t count) { stack_.template Reserve(count); } Ch* Push(size_t count) { return stack_.template Push(count); } + Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe(count); } void Pop(size_t count) { stack_.template Pop(count); } const Ch* GetString() const { @@ -68,8 +78,12 @@ public: return stack_.template Bottom(); } + //! Get the size of string in bytes in the string buffer. size_t GetSize() const { return stack_.GetSize(); } + //! Get the length of string in Ch in the string buffer. + size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); } + static const size_t kDefaultCapacity = 256; mutable internal::Stack stack_; @@ -82,6 +96,16 @@ private: //! String buffer with UTF8 encoding typedef GenericStringBuffer > StringBuffer; +template +inline void PutReserve(GenericStringBuffer& stream, size_t count) { + stream.Reserve(count); +} + +template +inline void PutUnsafe(GenericStringBuffer& stream, typename Encoding::Ch c) { + stream.PutUnsafe(c); +} + //! Implement specialized version of PutN() with memset() for better performance. template<> inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { @@ -90,4 +114,8 @@ inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { RAPIDJSON_NAMESPACE_END +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + #endif // RAPIDJSON_STRINGBUFFER_H_ diff --git a/contrib/rapidjson/include/rapidjson/writer.h b/contrib/rapidjson/include/rapidjson/writer.h index e1eea38b9..e610ebb60 100644 --- a/contrib/rapidjson/include/rapidjson/writer.h +++ b/contrib/rapidjson/include/rapidjson/writer.h @@ -15,7 +15,8 @@ #ifndef RAPIDJSON_WRITER_H_ #define RAPIDJSON_WRITER_H_ -#include "rapidjson.h" +#include "stream.h" +#include "internal/meta.h" #include "internal/stack.h" #include "internal/strfunc.h" #include "internal/dtoa.h" @@ -23,8 +24,16 @@ #include "stringbuffer.h" #include // placement new -#if RAPIDJSON_HAS_STDSTRING -#include +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#elif defined(RAPIDJSON_NEON) +#include #endif #ifdef _MSC_VER @@ -32,8 +41,36 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant #endif +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + RAPIDJSON_NAMESPACE_BEGIN +/////////////////////////////////////////////////////////////////////////////// +// WriteFlag + +/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kWriteDefaultFlags definition. + + User can define this as any \c WriteFlag combinations. +*/ +#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS +#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags +#endif + +//! Combination of writeFlags +enum WriteFlag { + kWriteNoFlags = 0, //!< No flags are set. + kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings. + kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN. + kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS +}; + //! JSON writer /*! Writer implements the concept Handler. It generates JSON text by events to an output os. @@ -50,11 +87,13 @@ RAPIDJSON_NAMESPACE_BEGIN \tparam StackAllocator Type of allocator for allocating memory of stack. \note implements Handler concept */ -template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator> +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> class Writer { public: typedef typename SourceEncoding::Ch Ch; + static const int kDefaultMaxDecimalPlaces = 324; + //! Constructor /*! \param os Output stream. \param stackAllocator User supplied allocator. If it is null, it will create a private one. @@ -62,11 +101,18 @@ public: */ explicit Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : - os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), hasRoot_(false) {} + os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} explicit Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : - os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), hasRoot_(false) {} + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Writer(Writer&& rhs) : + os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) { + rhs.os_ = 0; + } +#endif //! Reset the writer with a new stream. /*! @@ -100,29 +146,66 @@ public: return hasRoot_ && level_stack_.Empty(); } + int GetMaxDecimalPlaces() const { + return maxDecimalPlaces_; + } + + //! Sets the maximum number of decimal places for double output. + /*! + This setting truncates the output with specified number of decimal places. + + For example, + + \code + writer.SetMaxDecimalPlaces(3); + writer.StartArray(); + writer.Double(0.12345); // "0.123" + writer.Double(0.0001); // "0.0" + writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent) + writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent) + writer.EndArray(); + \endcode + + The default setting does not truncate any decimal places. You can restore to this setting by calling + \code + writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces); + \endcode + */ + void SetMaxDecimalPlaces(int maxDecimalPlaces) { + maxDecimalPlaces_ = maxDecimalPlaces; + } + /*!@name Implementation of Handler \see Handler */ //@{ - bool Null() { Prefix(kNullType); return WriteNull(); } - bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return WriteBool(b); } - bool Int(int i) { Prefix(kNumberType); return WriteInt(i); } - bool Uint(unsigned u) { Prefix(kNumberType); return WriteUint(u); } - bool Int64(int64_t i64) { Prefix(kNumberType); return WriteInt64(i64); } - bool Uint64(uint64_t u64) { Prefix(kNumberType); return WriteUint64(u64); } + bool Null() { Prefix(kNullType); return EndValue(WriteNull()); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); } + bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); } + bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); } + bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(WriteUint64(u64)); } //! Writes the given \c double value to the stream /*! \param d The value to be written. \return Whether it is succeed. */ - bool Double(double d) { Prefix(kNumberType); return WriteDouble(d); } + bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + Prefix(kNumberType); + return EndValue(WriteString(str, length)); + } bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); (void)copy; Prefix(kStringType); - return WriteString(str, length); + return EndValue(WriteString(str, length)); } #if RAPIDJSON_HAS_STDSTRING @@ -138,16 +221,21 @@ public: } bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) + { + return Key(str.data(), SizeType(str.size())); + } +#endif bool EndObject(SizeType memberCount = 0) { (void)memberCount; - RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); - RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value level_stack_.template Pop(1); - bool ret = WriteEndObject(); - if (level_stack_.Empty()) // end of json text - os_->Flush(); - return ret; + return EndValue(WriteEndObject()); } bool StartArray() { @@ -161,10 +249,7 @@ public: RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); level_stack_.template Pop(1); - bool ret = WriteEndArray(); - if (level_stack_.Empty()) // end of json text - os_->Flush(); - return ret; + return EndValue(WriteEndArray()); } //@} @@ -172,11 +257,33 @@ public: //@{ //! Simpler but slower overload. - bool String(const Ch* str) { return String(str, internal::StrLen(str)); } - bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } - + bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); } + //@} + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + */ + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + Prefix(type); + return EndValue(WriteRawValue(json, length)); + } + + //! Flush the output stream. + /*! + Allows the user to flush the output stream immediately. + */ + void Flush() { + os_->Flush(); + } + protected: //! Information for each nested level struct Level { @@ -188,15 +295,18 @@ protected: static const size_t kDefaultLevelDepth = 32; bool WriteNull() { - os_->Put('n'); os_->Put('u'); os_->Put('l'); os_->Put('l'); return true; + PutReserve(*os_, 4); + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true; } bool WriteBool(bool b) { if (b) { - os_->Put('t'); os_->Put('r'); os_->Put('u'); os_->Put('e'); + PutReserve(*os_, 4); + PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e'); } else { - os_->Put('f'); os_->Put('a'); os_->Put('l'); os_->Put('s'); os_->Put('e'); + PutReserve(*os_, 5); + PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e'); } return true; } @@ -204,45 +314,69 @@ protected: bool WriteInt(int i) { char buffer[11]; const char* end = internal::i32toa(i, buffer); + PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) - os_->Put(*p); + PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteUint(unsigned u) { char buffer[10]; const char* end = internal::u32toa(u, buffer); + PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) - os_->Put(*p); + PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteInt64(int64_t i64) { char buffer[21]; const char* end = internal::i64toa(i64, buffer); + PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) - os_->Put(*p); + PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteUint64(uint64_t u64) { char buffer[20]; char* end = internal::u64toa(u64, buffer); + PutReserve(*os_, static_cast(end - buffer)); for (char* p = buffer; p != end; ++p) - os_->Put(*p); + PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + if (!(writeFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + char buffer[25]; - char* end = internal::dtoa(d, buffer); + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + PutReserve(*os_, static_cast(end - buffer)); for (char* p = buffer; p != end; ++p) - os_->Put(*p); + PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteString(const Ch* str, SizeType length) { - static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const typename OutputStream::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; static const char escape[256] = { #define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //0 1 2 3 4 5 6 7 8 9 A B C D E F @@ -255,22 +389,27 @@ protected: #undef Z16 }; - os_->Put('\"'); + if (TargetEncoding::supportUnicode) + PutReserve(*os_, 2 + length * 6); // "\uxxxx..." + else + PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..." + + PutUnsafe(*os_, '\"'); GenericStringStream is(str); - while (is.Tell() < length) { + while (ScanWriteUnescapedString(is, length)) { const Ch c = is.Peek(); - if (!TargetEncoding::supportUnicode && (unsigned)c >= 0x80) { + if (!TargetEncoding::supportUnicode && static_cast(c) >= 0x80) { // Unicode escaping unsigned codepoint; - if (!SourceEncoding::Decode(is, &codepoint)) + if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint))) return false; - os_->Put('\\'); - os_->Put('u'); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { - os_->Put(hexDigits[(codepoint >> 12) & 15]); - os_->Put(hexDigits[(codepoint >> 8) & 15]); - os_->Put(hexDigits[(codepoint >> 4) & 15]); - os_->Put(hexDigits[(codepoint ) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint ) & 15]); } else { RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); @@ -278,45 +417,59 @@ protected: unsigned s = codepoint - 0x010000; unsigned lead = (s >> 10) + 0xD800; unsigned trail = (s & 0x3FF) + 0xDC00; - os_->Put(hexDigits[(lead >> 12) & 15]); - os_->Put(hexDigits[(lead >> 8) & 15]); - os_->Put(hexDigits[(lead >> 4) & 15]); - os_->Put(hexDigits[(lead ) & 15]); - os_->Put('\\'); - os_->Put('u'); - os_->Put(hexDigits[(trail >> 12) & 15]); - os_->Put(hexDigits[(trail >> 8) & 15]); - os_->Put(hexDigits[(trail >> 4) & 15]); - os_->Put(hexDigits[(trail ) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(lead ) & 15]); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(trail ) & 15]); } } - else if ((sizeof(Ch) == 1 || (unsigned)c < 256) && escape[(unsigned char)c]) { + else if ((sizeof(Ch) == 1 || static_cast(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { is.Take(); - os_->Put('\\'); - os_->Put(escape[(unsigned char)c]); - if (escape[(unsigned char)c] == 'u') { - os_->Put('0'); - os_->Put('0'); - os_->Put(hexDigits[(unsigned char)c >> 4]); - os_->Put(hexDigits[(unsigned char)c & 0xF]); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, static_cast(escape[static_cast(c)])); + if (escape[static_cast(c)] == 'u') { + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, hexDigits[static_cast(c) >> 4]); + PutUnsafe(*os_, hexDigits[static_cast(c) & 0xF]); } } - else - if (!Transcoder::Transcode(is, *os_)) - return false; + else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; } - os_->Put('\"'); + PutUnsafe(*os_, '\"'); return true; } + bool ScanWriteUnescapedString(GenericStringStream& is, size_t length) { + return RAPIDJSON_LIKELY(is.Tell() < length); + } + bool WriteStartObject() { os_->Put('{'); return true; } bool WriteEndObject() { os_->Put('}'); return true; } bool WriteStartArray() { os_->Put('['); return true; } bool WriteEndArray() { os_->Put(']'); return true; } + bool WriteRawValue(const Ch* json, size_t length) { + PutReserve(*os_, length); + for (size_t i = 0; i < length; i++) { + RAPIDJSON_ASSERT(json[i] != '\0'); + PutUnsafe(*os_, json[i]); + } + return true; + } + void Prefix(Type type) { (void)type; - if (level_stack_.GetSize() != 0) { // this value is not at root + if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root Level* level = level_stack_.template Top(); if (level->valueCount > 0) { if (level->inArray) @@ -334,8 +487,16 @@ protected: } } + // Flush the value if it is the top level one. + bool EndValue(bool ret) { + if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text + Flush(); + return ret; + } + OutputStream* os_; internal::Stack level_stack_; + int maxDecimalPlaces_; bool hasRoot_; private: @@ -350,7 +511,7 @@ template<> inline bool Writer::WriteInt(int i) { char *buffer = os_->Push(11); const char* end = internal::i32toa(i, buffer); - os_->Pop(11 - (end - buffer)); + os_->Pop(static_cast(11 - (end - buffer))); return true; } @@ -358,7 +519,7 @@ template<> inline bool Writer::WriteUint(unsigned u) { char *buffer = os_->Push(10); const char* end = internal::u32toa(u, buffer); - os_->Pop(10 - (end - buffer)); + os_->Pop(static_cast(10 - (end - buffer))); return true; } @@ -366,7 +527,7 @@ template<> inline bool Writer::WriteInt64(int64_t i64) { char *buffer = os_->Push(21); const char* end = internal::i64toa(i64, buffer); - os_->Pop(21 - (end - buffer)); + os_->Pop(static_cast(21 - (end - buffer))); return true; } @@ -374,22 +535,177 @@ template<> inline bool Writer::WriteUint64(uint64_t u) { char *buffer = os_->Push(20); const char* end = internal::u64toa(u, buffer); - os_->Pop(20 - (end - buffer)); + os_->Pop(static_cast(20 - (end - buffer))); return true; } template<> inline bool Writer::WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag). + if (!(kWriteDefaultFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + char *buffer = os_->Push(25); - char* end = internal::dtoa(d, buffer); - os_->Pop(25 - (end - buffer)); + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + os_->Pop(static_cast(25 - (end - buffer))); return true; } +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (; p != endAligned; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType len; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + len = offset; +#else + len = static_cast(__builtin_ffs(r) - 1); +#endif + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#elif defined(RAPIDJSON_NEON) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (; p != endAligned; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + SizeType len = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + unsigned lz = (unsigned)__builtin_clzll(high); + len = 8 + (lz >> 3); + escaped = true; + } + } else { + unsigned lz = (unsigned)__builtin_clzll(low); + len = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + vst1q_u8(reinterpret_cast(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#endif // RAPIDJSON_NEON + RAPIDJSON_NAMESPACE_END #ifdef _MSC_VER RAPIDJSON_DIAG_POP #endif +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + #endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/contrib/rapidjson/license.txt b/contrib/rapidjson/license.txt index 879293afa..7ccc161c8 100644 --- a/contrib/rapidjson/license.txt +++ b/contrib/rapidjson/license.txt @@ -3,7 +3,7 @@ Tencent is pleased to support the open source community by making RapidJSON avai Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. If you have downloaded a copy of the RapidJSON binary from Tencent, please note that the RapidJSON binary is licensed under the MIT License. -If you have downloaded a copy of the RapidJSON source code from Tencent, please note that RapidJSON source code is licensed under the MIT License, except for the third-party components listed below which are subject to different license terms. Your integration of RapidJSON into your own projects may require compliance with the MIT License, as well as the other licenses applicable to the third-party components included within RapidJSON. +If you have downloaded a copy of the RapidJSON source code from Tencent, please note that RapidJSON source code is licensed under the MIT License, except for the third-party components listed below which are subject to different license terms. Your integration of RapidJSON into your own projects may require compliance with the MIT License, as well as the other licenses applicable to the third-party components included within RapidJSON. To avoid the problematic JSON license in your own projects, it's sufficient to exclude the bin/jsonchecker/ directory, as it's the only code under the JSON license. A copy of the MIT License is included in this file. Other dependencies and licenses: diff --git a/contrib/rapidjson/readme.md b/contrib/rapidjson/readme.md index 19da38667..b833a98e8 100644 --- a/contrib/rapidjson/readme.md +++ b/contrib/rapidjson/readme.md @@ -1,17 +1,17 @@ -![](doc/logo/rapidjson.png) +![RapidJSON logo](doc/logo/rapidjson.png) -![](https://img.shields.io/badge/release-v1.0.2-blue.png) +![Release version](https://img.shields.io/badge/release-v1.1.0-blue.svg) -## A fast JSON parser/generator for C++ with both SAX/DOM style API +## A fast JSON parser/generator for C++ with both SAX/DOM style API Tencent is pleased to support the open source community by making RapidJSON available. Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. -* [RapidJSON GitHub](https://github.com/miloyip/rapidjson/) +* [RapidJSON GitHub](https://github.com/Tencent/rapidjson/) * RapidJSON Documentation - * [English](http://miloyip.github.io/rapidjson/) - * [简体中文](http://miloyip.github.io/rapidjson/zh-cn/) + * [English](http://rapidjson.org/) + * [简体中文](http://rapidjson.org/zh-cn/) * [GitBook](https://www.gitbook.com/book/miloyip/rapidjson/) with downloadable PDF/EPUB/MOBI, without API reference. ## Build status @@ -20,33 +20,43 @@ Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights | :---------------: | :-----------------: | :-------------------: | | ![lin-badge] | ![win-badge] | ![cov-badge] | -[lin-badge]: https://travis-ci.org/miloyip/rapidjson.png?branch=master "Travis build status" -[lin-link]: https://travis-ci.org/miloyip/rapidjson "Travis build status" -[win-badge]: https://ci.appveyor.com/api/projects/status/u658dcuwxo14a8m9/branch/master "AppVeyor build status" -[win-link]: https://ci.appveyor.com/project/miloyip/rapidjson/branch/master "AppVeyor build status" -[cov-badge]: https://coveralls.io/repos/miloyip/rapidjson/badge.png?branch=master -[cov-link]: https://coveralls.io/r/miloyip/rapidjson?branch=master +[lin-badge]: https://travis-ci.org/Tencent/rapidjson.svg?branch=master "Travis build status" +[lin-link]: https://travis-ci.org/Tencent/rapidjson "Travis build status" +[win-badge]: https://ci.appveyor.com/api/projects/status/l6qulgqahcayidrf/branch/master?svg=true "AppVeyor build status" +[win-link]: https://ci.appveyor.com/project/miloyip/rapidjson-0fdqj/branch/master "AppVeyor build status" +[cov-badge]: https://coveralls.io/repos/Tencent/rapidjson/badge.svg?branch=master "Coveralls coverage" +[cov-link]: https://coveralls.io/r/Tencent/rapidjson?branch=master "Coveralls coverage" ## Introduction RapidJSON is a JSON parser and generator for C++. It was inspired by [RapidXml](http://rapidxml.sourceforge.net/). -* RapidJSON is small but complete. It supports both SAX and DOM style API. The SAX parser is only a half thousand lines of code. +* RapidJSON is **small** but **complete**. It supports both SAX and DOM style API. The SAX parser is only a half thousand lines of code. -* RapidJSON is fast. Its performance can be comparable to `strlen()`. It also optionally supports SSE2/SSE4.2 for acceleration. +* RapidJSON is **fast**. Its performance can be comparable to `strlen()`. It also optionally supports SSE2/SSE4.2 for acceleration. -* RapidJSON is self-contained. It does not depend on external libraries such as BOOST. It even does not depend on STL. +* RapidJSON is **self-contained** and **header-only**. It does not depend on external libraries such as BOOST. It even does not depend on STL. -* RapidJSON is memory friendly. Each JSON value occupies exactly 16/20 bytes for most 32/64-bit machines (excluding text string). By default it uses a fast memory allocator, and the parser allocates memory compactly during parsing. +* RapidJSON is **memory-friendly**. Each JSON value occupies exactly 16 bytes for most 32/64-bit machines (excluding text string). By default it uses a fast memory allocator, and the parser allocates memory compactly during parsing. -* RapidJSON is Unicode friendly. It supports UTF-8, UTF-16, UTF-32 (LE & BE), and their detection, validation and transcoding internally. For example, you can read a UTF-8 file and let RapidJSON transcode the JSON strings into UTF-16 in the DOM. It also supports surrogates and "\u0000" (null character). +* RapidJSON is **Unicode-friendly**. It supports UTF-8, UTF-16, UTF-32 (LE & BE), and their detection, validation and transcoding internally. For example, you can read a UTF-8 file and let RapidJSON transcode the JSON strings into UTF-16 in the DOM. It also supports surrogates and "\u0000" (null character). More features can be read [here](doc/features.md). -JSON(JavaScript Object Notation) is a light-weight data exchange format. RapidJSON should be in fully compliance with RFC7159/ECMA-404. More information about JSON can be obtained at +JSON(JavaScript Object Notation) is a light-weight data exchange format. RapidJSON should be in fully compliance with RFC7159/ECMA-404, with optional support of relaxed syntax. More information about JSON can be obtained at * [Introducing JSON](http://json.org/) -* [RFC7159: The JavaScript Object Notation (JSON) Data Interchange Format](http://www.ietf.org/rfc/rfc7159.txt) -* [Standard ECMA-404: The JSON Data Interchange Format](http://www.ecma-international.org/publications/standards/Ecma-404.htm) +* [RFC7159: The JavaScript Object Notation (JSON) Data Interchange Format](https://tools.ietf.org/html/rfc7159) +* [Standard ECMA-404: The JSON Data Interchange Format](https://www.ecma-international.org/publications/standards/Ecma-404.htm) + +## Highlights in v1.1 (2016-8-25) + +* Added [JSON Pointer](doc/pointer.md) +* Added [JSON Schema](doc/schema.md) +* Added [relaxed JSON syntax](doc/dom.md) (comment, trailing comma, NaN/Infinity) +* Iterating array/object with [C++11 Range-based for loop](doc/tutorial.md) +* Reduce memory overhead of each `Value` from 24 bytes to 16 bytes in x86-64 architecture. + +For other changes please refer to [change log](CHANGELOG.md). ## Compatibility @@ -63,9 +73,9 @@ Users can build and run the unit tests on their platform/compiler. RapidJSON is a header-only C++ library. Just copy the `include/rapidjson` folder to system or project's include path. RapidJSON uses following software as its dependencies: -* [CMake](http://www.cmake.org) as a general build tool -* (optional)[Doxygen](http://www.doxygen.org) to build documentation -* (optional)[googletest](https://code.google.com/p/googletest/) for unit and performance testing +* [CMake](https://cmake.org/) as a general build tool +* (optional) [Doxygen](http://www.doxygen.org) to build documentation +* (optional) [googletest](https://github.com/google/googletest) for unit and performance testing To generate user documentation and run tests please proceed with the steps below: @@ -74,7 +84,7 @@ To generate user documentation and run tests please proceed with the steps below 3. Change to `build` directory and run `cmake ..` command to configure your build. Windows users can do the same with cmake-gui application. 4. On Windows, build the solution found in the build directory. On Linux, run `make` from the build directory. -On successfull build you will find compiled test and example binaries in `bin` +On successful build you will find compiled test and example binaries in `bin` directory. The generated documentation will be available in `doc/html` directory of the build tree. To run tests after finished build please run `make test` or `ctest` from your build tree. You can get detailed output using `ctest @@ -126,4 +136,25 @@ The following diagram shows the process. ![simpledom](doc/diagram/simpledom.png) -More [examples](https://github.com/miloyip/rapidjson/tree/master/example) are available. +More [examples](https://github.com/Tencent/rapidjson/tree/master/example) are available: + +* DOM API + * [tutorial](https://github.com/Tencent/rapidjson/blob/master/example/tutorial/tutorial.cpp): Basic usage of DOM API. + +* SAX API + * [simplereader](https://github.com/Tencent/rapidjson/blob/master/example/simplereader/simplereader.cpp): Dumps all SAX events while parsing a JSON by `Reader`. + * [condense](https://github.com/Tencent/rapidjson/blob/master/example/condense/condense.cpp): A command line tool to rewrite a JSON, with all whitespaces removed. + * [pretty](https://github.com/Tencent/rapidjson/blob/master/example/pretty/pretty.cpp): A command line tool to rewrite a JSON with indents and newlines by `PrettyWriter`. + * [capitalize](https://github.com/Tencent/rapidjson/blob/master/example/capitalize/capitalize.cpp): A command line tool to capitalize strings in JSON. + * [messagereader](https://github.com/Tencent/rapidjson/blob/master/example/messagereader/messagereader.cpp): Parse a JSON message with SAX API. + * [serialize](https://github.com/Tencent/rapidjson/blob/master/example/serialize/serialize.cpp): Serialize a C++ object into JSON with SAX API. + * [jsonx](https://github.com/Tencent/rapidjson/blob/master/example/jsonx/jsonx.cpp): Implements a `JsonxWriter` which stringify SAX events into [JSONx](https://www-01.ibm.com/support/knowledgecenter/SS9H2Y_7.1.0/com.ibm.dp.doc/json_jsonx.html) (a kind of XML) format. The example is a command line tool which converts input JSON into JSONx format. + +* Schema + * [schemavalidator](https://github.com/Tencent/rapidjson/blob/master/example/schemavalidator/schemavalidator.cpp) : A command line tool to validate a JSON with a JSON schema. + +* Advanced + * [prettyauto](https://github.com/Tencent/rapidjson/blob/master/example/prettyauto/prettyauto.cpp): A modified version of [pretty](https://github.com/Tencent/rapidjson/blob/master/example/pretty/pretty.cpp) to automatically handle JSON with any UTF encodings. + * [parsebyparts](https://github.com/Tencent/rapidjson/blob/master/example/parsebyparts/parsebyparts.cpp): Implements an `AsyncDocumentParser` which can parse JSON in parts, using C++11 thread. + * [filterkey](https://github.com/Tencent/rapidjson/blob/master/example/filterkey/filterkey.cpp): A command line tool to remove all values with user-specified key. + * [filterkeydom](https://github.com/Tencent/rapidjson/blob/master/example/filterkeydom/filterkeydom.cpp): Same tool as above, but it demonstrates how to use a generator to populate a `Document`. From 9eef4c16a828f35900f9f97a46e85296515c631a Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 11:33:27 +0300 Subject: [PATCH 172/490] utMetadata: Fix memory leak --- test/unit/utMetadata.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/utMetadata.cpp b/test/unit/utMetadata.cpp index b44de1eb2..4109b068c 100644 --- a/test/unit/utMetadata.cpp +++ b/test/unit/utMetadata.cpp @@ -79,6 +79,7 @@ TEST_F( utMetadata, allocTest ) { EXPECT_EQ( 1U, data->mNumProperties ); EXPECT_NE( nullptr, data->mKeys ); EXPECT_NE( nullptr, data->mValues ); + aiMetadata::Dealloc( data ); } TEST_F( utMetadata, get_set_pod_Test ) { From 1bee5b002564ef9b7b3f765a1247ae8991e60123 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 11:55:58 +0300 Subject: [PATCH 173/490] utRemoveVCProcess: Fix memory leak --- test/unit/utRemoveVCProcess.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/utRemoveVCProcess.cpp b/test/unit/utRemoveVCProcess.cpp index c78e80d3f..6caa72c11 100644 --- a/test/unit/utRemoveVCProcess.cpp +++ b/test/unit/utRemoveVCProcess.cpp @@ -70,7 +70,7 @@ TEST_F( utRevmoveVCProcess, issue1266_ProcessMeshTest_NoCrash ) { mesh->mNumVertices = 1; mesh->mColors[ 0 ] = new aiColor4D[ 2 ]; scene->mMeshes[ 0 ] = mesh; - RemoveVCProcess *process = new RemoveVCProcess; + std::unique_ptr process(new RemoveVCProcess); process->Execute( scene ); delete scene; } From 1f16ed9fd0956e73f8ff59a90204b476a56e8a2a Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 11:56:13 +0300 Subject: [PATCH 174/490] UnrealLoader: Fix IOStream leak --- code/UnrealLoader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/UnrealLoader.cpp b/code/UnrealLoader.cpp index c8382cb01..a79a2a5c5 100644 --- a/code/UnrealLoader.cpp +++ b/code/UnrealLoader.cpp @@ -157,7 +157,7 @@ void UnrealImporter::InternReadFile( const std::string& pFile, DefaultLogger::get()->debug("UNREAL: uc file is " + uc_path); // and open the files ... we can't live without them - IOStream* p = pIOHandler->Open(d_path); + std::unique_ptr p(pIOHandler->Open(d_path)); if (!p) throw DeadlyImportError("UNREAL: Unable to open _d file"); StreamReaderLE d_reader(pIOHandler->Open(d_path)); @@ -203,7 +203,7 @@ void UnrealImporter::InternReadFile( const std::string& pFile, d_reader.IncPtr(1); } - p = pIOHandler->Open(a_path); + p.reset(pIOHandler->Open(a_path)); if (!p) throw DeadlyImportError("UNREAL: Unable to open _a file"); StreamReaderLE a_reader(pIOHandler->Open(a_path)); From 61278aa408e77f81a3c2d0f2f894dd5b1c667d9f Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 12:09:26 +0300 Subject: [PATCH 175/490] AMFImporter: Fix memory leak --- code/AMFImporter_Postprocess.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/code/AMFImporter_Postprocess.cpp b/code/AMFImporter_Postprocess.cpp index 085336c51..789a11eb8 100644 --- a/code/AMFImporter_Postprocess.cpp +++ b/code/AMFImporter_Postprocess.cpp @@ -686,7 +686,6 @@ std::list mesh_idx; tmesh->mNumVertices = static_cast(vert_arr.size()); tmesh->mVertices = new aiVector3D[tmesh->mNumVertices]; tmesh->mColors[0] = new aiColor4D[tmesh->mNumVertices]; - tmesh->mFaces = new aiFace[face_list_cur.size()]; memcpy(tmesh->mVertices, vert_arr.data(), tmesh->mNumVertices * sizeof(aiVector3D)); memcpy(tmesh->mColors[0], col_arr.data(), tmesh->mNumVertices * sizeof(aiColor4D)); From 22b55d01a20d47def5cb7c5b204271a845bc3f7a Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 12:35:17 +0300 Subject: [PATCH 176/490] OpenGEXImporter: Store ChildInfo in unique_ptr so they get automatically cleaned up --- code/OpenGEXImporter.cpp | 8 ++++---- code/OpenGEXImporter.h | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index d9c58e91c..0d2ed0520 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -1231,9 +1231,9 @@ void OpenGEXImporter::pushNode( aiNode *node, aiScene *pScene ) { if( m_nodeChildMap.end() == it ) { info = new ChildInfo; m_root = info; - m_nodeChildMap[ node->mParent ] = info; + m_nodeChildMap[ node->mParent ] = std::unique_ptr(info); } else { - info = it->second; + info = it->second.get(); } info->m_children.push_back( node ); } else { @@ -1243,9 +1243,9 @@ void OpenGEXImporter::pushNode( aiNode *node, aiScene *pScene ) { NodeChildMap::iterator it( m_nodeChildMap.find( node->mParent ) ); if( m_nodeChildMap.end() == it ) { info = new ChildInfo; - m_nodeChildMap[ node->mParent ] = info; + m_nodeChildMap[ node->mParent ] = std::unique_ptr(info); } else { - info = it->second; + info = it->second.get(); } info->m_children.push_back( node ); } diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index bb6b45140..948712869 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include namespace ODDLParser { class DDLNode; @@ -179,7 +180,7 @@ private: std::list m_children; }; ChildInfo *m_root; - typedef std::map NodeChildMap; + typedef std::map > NodeChildMap; NodeChildMap m_nodeChildMap; std::vector m_meshCache; From 775f984d99a4bce513f5ce712d77f38116faef1e Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 12:38:44 +0300 Subject: [PATCH 177/490] OpenGEXImporter: Fix IOStream leak --- code/OpenGEXImporter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 0d2ed0520..24dabd1d4 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -307,6 +307,7 @@ void OpenGEXImporter::InternReadFile( const std::string &filename, aiScene *pSce std::vector buffer; TextFileToBuffer( file, buffer ); + pIOHandler->Close( file ); OpenDDLParser myParser; myParser.setBuffer( &buffer[ 0 ], buffer.size() ); From b841ed194bf833dd1f8baf033d54f00f7b2c5095 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 12:43:49 +0300 Subject: [PATCH 178/490] OpenGEXImporter: Store RefInfo in unique_ptr so they get automatically cleaned up --- code/OpenGEXImporter.cpp | 8 ++++---- code/OpenGEXImporter.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 24dabd1d4..7073a5e76 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -525,7 +525,7 @@ void OpenGEXImporter::handleObjectRefNode( DDLNode *node, aiScene *pScene ) { m_currentNode->mNumMeshes = static_cast(objRefNames.size()); m_currentNode->mMeshes = new unsigned int[ objRefNames.size() ]; if ( !objRefNames.empty() ) { - m_unresolvedRefStack.push_back( new RefInfo( m_currentNode, RefInfo::MeshRef, objRefNames ) ); + m_unresolvedRefStack.push_back( std::unique_ptr( new RefInfo( m_currentNode, RefInfo::MeshRef, objRefNames ) ) ); } } else if ( m_tokenType == Grammar::LightNodeToken ) { // TODO! @@ -544,7 +544,7 @@ void OpenGEXImporter::handleMaterialRefNode( ODDLParser::DDLNode *node, aiScene std::vector matRefNames; getRefNames( node, matRefNames ); if( !matRefNames.empty() ) { - m_unresolvedRefStack.push_back( new RefInfo( m_currentNode, RefInfo::MaterialRef, matRefNames ) ); + m_unresolvedRefStack.push_back( std::unique_ptr( new RefInfo( m_currentNode, RefInfo::MaterialRef, matRefNames ) ) ); } } @@ -1165,8 +1165,8 @@ void OpenGEXImporter::resolveReferences() { } RefInfo *currentRefInfo( nullptr ); - for( std::vector::iterator it = m_unresolvedRefStack.begin(); it != m_unresolvedRefStack.end(); ++it ) { - currentRefInfo = *it; + for( auto it = m_unresolvedRefStack.begin(); it != m_unresolvedRefStack.end(); ++it ) { + currentRefInfo = it->get(); if( nullptr != currentRefInfo ) { aiNode *node( currentRefInfo->m_node ); if( RefInfo::MeshRef == currentRefInfo->m_type ) { diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index 948712869..aefd9ef1d 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -201,7 +201,7 @@ private: std::vector m_cameraCache; std::vector m_lightCache; std::vector m_nodeStack; - std::vector m_unresolvedRefStack; + std::vector > m_unresolvedRefStack; }; } // Namespace OpenGEX From 50b43f76e12e269be8bb7e5985115bc0e5d4d1b7 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Thu, 5 Oct 2017 12:51:08 +0300 Subject: [PATCH 179/490] OpenGEXImporter: Copy materials to scene --- code/OpenGEXImporter.cpp | 14 ++++++++++++++ code/OpenGEXImporter.h | 1 + 2 files changed, 15 insertions(+) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 7073a5e76..47e3e3e8c 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -322,6 +322,7 @@ void OpenGEXImporter::InternReadFile( const std::string &filename, aiScene *pSce copyMeshes( pScene ); copyCameras( pScene ); copyLights( pScene ); + copyMaterials( pScene ); resolveReferences(); createNodeTree( pScene ); } @@ -1158,6 +1159,19 @@ void OpenGEXImporter::copyLights( aiScene *pScene ) { std::copy( m_lightCache.begin(), m_lightCache.end(), pScene->mLights ); } +//------------------------------------------------------------------------------------------------ +void OpenGEXImporter::copyMaterials( aiScene *pScene ) { + ai_assert( nullptr != pScene ); + + if ( m_materialCache.empty() ) { + return; + } + + pScene->mNumMaterials = static_cast(m_materialCache.size()); + pScene->mMaterials = new aiMaterial*[ pScene->mNumMaterials ]; + std::copy( m_materialCache.begin(), m_materialCache.end(), pScene->mMaterials ); +} + //------------------------------------------------------------------------------------------------ void OpenGEXImporter::resolveReferences() { if( m_unresolvedRefStack.empty() ) { diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index aefd9ef1d..c0cde579c 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -133,6 +133,7 @@ protected: void copyMeshes( aiScene *pScene ); void copyCameras( aiScene *pScene ); void copyLights( aiScene *pScene ); + void copyMaterials( aiScene *pScene ); void resolveReferences(); void pushNode( aiNode *node, aiScene *pScene ); aiNode *popNode(); From 326158633b7d6e495096af81c751ee0ea69a002e Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Fri, 6 Oct 2017 20:32:33 +1100 Subject: [PATCH 180/490] Fixed warnings on MSVC caused by implicit conversions from double to float. --- code/glTF2Asset.inl | 4 ++-- code/glTF2Exporter.cpp | 6 +++--- code/glTFExporter.cpp | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 3082ebfab..8b50fa1d3 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -724,7 +724,7 @@ namespace { SetTextureProperties(r, prop, out); if (Value* scale = FindNumber(*prop, "scale")) { - out.scale = scale->GetDouble(); + out.scale = static_cast(scale->GetDouble()); } } } @@ -735,7 +735,7 @@ namespace { SetTextureProperties(r, prop, out); if (Value* strength = FindNumber(*prop, "strength")) { - out.strength = strength->GetDouble(); + out.strength = static_cast(strength->GetDouble()); } } } diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 4ed8f20ff..8f46f7dc7 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -563,7 +563,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref& meshRef, Ref(jointNamesIndex); vertexWeightData[vertexId][jointsPerVertex[vertexId]] = vertWeight; jointsPerVertex[vertexId] += 1; @@ -872,7 +872,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, RefmNumPositionKeys / numKeyframes; // mTime is measured in ticks, but GLTF time is measured in seconds, so convert. // Check if we have to cast type here. e.g. uint16_t() - timeData[i] = nodeChannel->mPositionKeys[frameIndex].mTime / ticksPerSecond; + timeData[i] = static_cast(nodeChannel->mPositionKeys[frameIndex].mTime / ticksPerSecond); } Ref timeAccessor = ExportData(mAsset, animId, buffer, static_cast(numKeyframes), &timeData[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_FLOAT); @@ -953,7 +953,7 @@ void glTF2Exporter::ExportAnimations() Ref animRef = mAsset->animations.Create(name); // Parameters - ExtractAnimationData(*mAsset, name, animRef, bufferRef, nodeChannel, anim->mTicksPerSecond); + ExtractAnimationData(*mAsset, name, animRef, bufferRef, nodeChannel, static_cast(anim->mTicksPerSecond)); for (unsigned int j = 0; j < 3; ++j) { std::string channelType; diff --git a/code/glTFExporter.cpp b/code/glTFExporter.cpp index a5df09ac7..92abcc15c 100644 --- a/code/glTFExporter.cpp +++ b/code/glTFExporter.cpp @@ -473,7 +473,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref& meshRef, Ref(jointNamesIndex); vertexWeightData[vertexId][jointsPerVertex[vertexId]] = vertWeight; jointsPerVertex[vertexId] += 1; @@ -872,7 +872,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, RefmNumPositionKeys / numKeyframes; // mTime is measured in ticks, but GLTF time is measured in seconds, so convert. // Check if we have to cast type here. e.g. uint16_t() - timeData[i] = nodeChannel->mPositionKeys[frameIndex].mTime / ticksPerSecond; + timeData[i] = static_cast(nodeChannel->mPositionKeys[frameIndex].mTime / ticksPerSecond); } Ref timeAccessor = ExportData(mAsset, animId, buffer, static_cast(numKeyframes), &timeData[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_FLOAT); @@ -953,7 +953,7 @@ void glTFExporter::ExportAnimations() Ref animRef = mAsset->animations.Create(name); /******************* Parameters ********************/ - ExtractAnimationData(*mAsset, name, animRef, bufferRef, nodeChannel, anim->mTicksPerSecond); + ExtractAnimationData(*mAsset, name, animRef, bufferRef, nodeChannel, static_cast(anim->mTicksPerSecond)); for (unsigned int j = 0; j < 3; ++j) { std::string channelType; From 9a79d243f9e61f83ef4ccc89e7532bb06701d244 Mon Sep 17 00:00:00 2001 From: Marco Di Benedetto Date: Sat, 7 Oct 2017 12:36:02 +0200 Subject: [PATCH 181/490] added additional displacement texture token. --- code/ObjFileMtlImporter.cpp | 49 +++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/code/ObjFileMtlImporter.cpp b/code/ObjFileMtlImporter.cpp index 36bb6c2cb..6ef655f8a 100644 --- a/code/ObjFileMtlImporter.cpp +++ b/code/ObjFileMtlImporter.cpp @@ -55,20 +55,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { -// Material specific token -static const std::string DiffuseTexture = "map_Kd"; -static const std::string AmbientTexture = "map_Ka"; -static const std::string SpecularTexture = "map_Ks"; -static const std::string OpacityTexture = "map_d"; -static const std::string EmissiveTexture = "map_emissive"; -static const std::string EmissiveTexture_1 = "map_Ke"; -static const std::string BumpTexture1 = "map_bump"; -static const std::string BumpTexture2 = "map_Bump"; -static const std::string BumpTexture3 = "bump"; -static const std::string NormalTexture = "map_Kn"; -static const std::string ReflectionTexture = "refl"; -static const std::string DisplacementTexture = "disp"; -static const std::string SpecularityTexture = "map_ns"; +// Material specific token (case insensitive compare) +static const std::string DiffuseTexture = "map_Kd"; +static const std::string AmbientTexture = "map_Ka"; +static const std::string SpecularTexture = "map_Ks"; +static const std::string OpacityTexture = "map_d"; +static const std::string EmissiveTexture1 = "map_emissive"; +static const std::string EmissiveTexture2 = "map_Ke"; +static const std::string BumpTexture1 = "map_bump"; +static const std::string BumpTexture2 = "bump"; +static const std::string NormalTexture = "map_Kn"; +static const std::string ReflectionTexture = "refl"; +static const std::string DisplacementTexture1 = "map_disp"; +static const std::string DisplacementTexture2 = "disp"; +static const std::string SpecularityTexture = "map_ns"; // texture option specific token static const std::string BlendUOption = "-blendu"; @@ -329,7 +329,7 @@ void ObjFileMtlImporter::getTexture() { // Ambient texture out = & m_pModel->m_pCurrentMaterial->textureAmbient; clampIndex = ObjFile::Material::TextureAmbientType; - } else if (!ASSIMP_strincmp( pPtr, SpecularTexture.c_str(), static_cast(SpecularTexture.size()) ) ) { + } else if ( !ASSIMP_strincmp( pPtr, SpecularTexture.c_str(), static_cast(SpecularTexture.size()) ) ) { // Specular texture out = & m_pModel->m_pCurrentMaterial->textureSpecular; clampIndex = ObjFile::Material::TextureSpecularType; @@ -337,33 +337,30 @@ void ObjFileMtlImporter::getTexture() { // Opacity texture out = & m_pModel->m_pCurrentMaterial->textureOpacity; clampIndex = ObjFile::Material::TextureOpacityType; - } else if (!ASSIMP_strincmp( pPtr, EmissiveTexture.c_str(), static_cast(EmissiveTexture.size()) ) ) { + } else if ( !ASSIMP_strincmp( pPtr, EmissiveTexture1.c_str(), static_cast(EmissiveTexture1.size()) ) || + !ASSIMP_strincmp( pPtr, EmissiveTexture2.c_str(), static_cast(EmissiveTexture2.size()) ) ) { // Emissive texture out = & m_pModel->m_pCurrentMaterial->textureEmissive; clampIndex = ObjFile::Material::TextureEmissiveType; - } else if ( !ASSIMP_strincmp( pPtr, EmissiveTexture_1.c_str(), static_cast(EmissiveTexture_1.size()) ) ) { - // Emissive texture - out = &m_pModel->m_pCurrentMaterial->textureEmissive; - clampIndex = ObjFile::Material::TextureEmissiveType; } else if ( !ASSIMP_strincmp( pPtr, BumpTexture1.c_str(), static_cast(BumpTexture1.size()) ) || - !ASSIMP_strincmp( pPtr, BumpTexture2.c_str(), static_cast(BumpTexture2.size()) ) || - !ASSIMP_strincmp( pPtr, BumpTexture3.c_str(), static_cast(BumpTexture3.size()) ) ) { + !ASSIMP_strincmp( pPtr, BumpTexture2.c_str(), static_cast(BumpTexture2.size()) ) ) { // Bump texture out = & m_pModel->m_pCurrentMaterial->textureBump; clampIndex = ObjFile::Material::TextureBumpType; - } else if (!ASSIMP_strincmp( pPtr,NormalTexture.c_str(), static_cast(NormalTexture.size()) ) ) { + } else if ( !ASSIMP_strincmp( pPtr,NormalTexture.c_str(), static_cast(NormalTexture.size()) ) ) { // Normal map out = & m_pModel->m_pCurrentMaterial->textureNormal; clampIndex = ObjFile::Material::TextureNormalType; - } else if(!ASSIMP_strincmp( pPtr, ReflectionTexture.c_str(), static_cast(ReflectionTexture.size()) ) ) { + } else if( !ASSIMP_strincmp( pPtr, ReflectionTexture.c_str(), static_cast(ReflectionTexture.size()) ) ) { // Reflection texture(s) //Do nothing here return; - } else if (!ASSIMP_strincmp( pPtr, DisplacementTexture.c_str(), static_cast(DisplacementTexture.size()) ) ) { + } else if ( !ASSIMP_strincmp( pPtr, DisplacementTexture1.c_str(), static_cast(DisplacementTexture1.size()) ) || + !ASSIMP_strincmp( pPtr, DisplacementTexture2.c_str(), static_cast(DisplacementTexture2.size()) ) ) { // Displacement texture out = &m_pModel->m_pCurrentMaterial->textureDisp; clampIndex = ObjFile::Material::TextureDispType; - } else if (!ASSIMP_strincmp( pPtr, SpecularityTexture.c_str(), static_cast(SpecularityTexture.size()) ) ) { + } else if ( !ASSIMP_strincmp( pPtr, SpecularityTexture.c_str(), static_cast(SpecularityTexture.size()) ) ) { // Specularity scaling (glossiness) out = & m_pModel->m_pCurrentMaterial->textureSpecularity; clampIndex = ObjFile::Material::TextureSpecularityType; From 530176825670851b91a27c847e39b82ea4c7b8c8 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 7 Oct 2017 14:33:34 +0300 Subject: [PATCH 182/490] Travis: Remove unused LINUX env variable --- .travis.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2eab656cc..62ba41606 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,22 +47,22 @@ matrix: include: - os: linux compiler: gcc - env: LINUX=1 TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON ASAN=OFF + env: TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON ASAN=OFF - os: linux compiler: gcc - env: LINUX=1 TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=ON + env: TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=ON - os: linux compiler: gcc - env: LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF + env: SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF - os: linux compiler: gcc - env: LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF + env: SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF - os: linux compiler: clang - env: LINUX=1 TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=ON + env: TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=ON - os: linux compiler: clang - env: LINUX=1 SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF + env: SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF install: - if [ $ANDROID ]; then wget -c http://dl.google.com/android/ndk/android-ndk-${PV}-${PLATF}.tar.bz2 && tar xf android-ndk-${PV}-${PLATF}.tar.bz2 ; fi From 54d14e6e3b92c6b8dbfe9ab0a6002af7a52ac995 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 7 Oct 2017 14:47:52 +0300 Subject: [PATCH 183/490] Travis: Refactor how we build CMake options --- .travis.sh | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/.travis.sh b/.travis.sh index 1ab1ee2b1..1c7c4e13e 100755 --- a/.travis.sh +++ b/.travis.sh @@ -1,6 +1,32 @@ function generate() { - cmake -G "Unix Makefiles" -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -DBUILD_SHARED_LIBS=$SHARED_BUILD -DASSIMP_COVERALLS=$ENABLE_COVERALLS -DASSIMP_WERROR=ON -DASSIMP_ASAN=$ASAN + OPTIONS="-DASSIMP_WERROR=ON" + + if [ "$TRAVIS_NO_EXPORT" = "YES" ] ; then + OPTIONS="$OPTIONS -DASSIMP_NO_EXPORT=YES" + else + OPTIONS="$OPTIONS -DASSIMP_NO_EXPORT=NO" + fi + + if [ "$SHARED_BUILD" = "ON" ] ; then + OPTIONS="$OPTIONS -DBUILD_SHARED_LIBS=ON" + else + OPTIONS="$OPTIONS -DBUILD_SHARED_LIBS=OFF" + fi + + if [ "$ENABLE_COVERALLS" = "ON" ] ; then + OPTIONS="$OPTIONS -DASSIMP_COVERALLS=ON" + else + OPTIONS="$OPTIONS -DASSIMP_COVERALLS=OFF" + fi + + if [ "$ASAN" = "ON" ] ; then + OPTIONS="$OPTIONS -DASSIMP_ASAN=ON" + else + OPTIONS="$OPTIONS -DASSIMP_ASAN=OFF" + fi + + cmake -G "Unix Makefiles" $OPTIONS } if [ $ANDROID ]; then From 42142105fa6f4142160d3df42f2200614ed90b76 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 7 Oct 2017 14:59:12 +0300 Subject: [PATCH 184/490] CMake: Be more verbose about enabled options --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0aae76837..c30278b7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,7 +108,10 @@ IF(MSVC) ENDIF(MSVC) IF(NOT BUILD_SHARED_LIBS) + MESSAGE(STATUS "Shared libraries disabled") SET(LINK_SEARCH_START_STATIC TRUE) +ELSE() + MESSAGE(STATUS "Shared libraries enabled") ENDIF(NOT BUILD_SHARED_LIBS) # Define here the needed parameters @@ -209,12 +212,14 @@ ELSEIF( CMAKE_COMPILER_IS_MINGW ) ENDIF() if (ASSIMP_COVERALLS) + MESSAGE(STATUS "Coveralls enabled") INCLUDE(Coveralls) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") endif() if (ASSIMP_WERROR) + MESSAGE(STATUS "Treating warnings as errors") IF (MSVC) add_compile_options(/WX) ELSE() @@ -224,6 +229,7 @@ if (ASSIMP_WERROR) endif() if (ASSIMP_ASAN) + MESSAGE(STATUS "AddressSanitizer enabled") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") endif() From 7037fdfe2c1edb62f10d6609b5f996d083b91841 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 7 Oct 2017 15:06:46 +0300 Subject: [PATCH 185/490] Travis: ASAN is now implicitly off, disable explicit off --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 62ba41606..47d9309b6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,22 +47,22 @@ matrix: include: - os: linux compiler: gcc - env: TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON ASAN=OFF + env: TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON - os: linux compiler: gcc env: TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=ON - os: linux compiler: gcc - env: SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF + env: SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF - os: linux compiler: gcc - env: SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF + env: SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF - os: linux compiler: clang env: TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=ON - os: linux compiler: clang - env: SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=OFF + env: SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF install: - if [ $ANDROID ]; then wget -c http://dl.google.com/android/ndk/android-ndk-${PV}-${PLATF}.tar.bz2 && tar xf android-ndk-${PV}-${PLATF}.tar.bz2 ; fi From ea58801a2dfdb922c3186a14d701c88544846072 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 7 Oct 2017 15:08:02 +0300 Subject: [PATCH 186/490] Travis: ENABLE_COVERALLS is now implicitly off, disable explicit off --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 47d9309b6..e7c02101d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,19 +50,19 @@ matrix: env: TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON - os: linux compiler: gcc - env: TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=ON + env: TRAVIS_NO_EXPORT=NO ASAN=ON - os: linux compiler: gcc - env: SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF + env: SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO - os: linux compiler: gcc - env: SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF + env: SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO - os: linux compiler: clang - env: TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF ASAN=ON + env: TRAVIS_NO_EXPORT=NO ASAN=ON - os: linux compiler: clang - env: SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO ENABLE_COVERALLS=OFF + env: SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO install: - if [ $ANDROID ]; then wget -c http://dl.google.com/android/ndk/android-ndk-${PV}-${PLATF}.tar.bz2 && tar xf android-ndk-${PV}-${PLATF}.tar.bz2 ; fi From f2cf8bf075d29f0abe175bccba39ec8542bbc79b Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 7 Oct 2017 15:08:41 +0300 Subject: [PATCH 187/490] Travis: Disable GCC AddressSanitizer build, Clang AddressSanitizer is strictly better --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index e7c02101d..8787d3765 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,9 +48,6 @@ matrix: - os: linux compiler: gcc env: TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON - - os: linux - compiler: gcc - env: TRAVIS_NO_EXPORT=NO ASAN=ON - os: linux compiler: gcc env: SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO From 69b8c1f60d9df235573cee84ee5d551f92e37a9c Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 7 Oct 2017 15:36:54 +0300 Subject: [PATCH 188/490] Travis: TRAVIS_NO_EXPORT is now implicitly off, disable explicit off --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8787d3765..8c9736ef8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,16 +50,16 @@ matrix: env: TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON - os: linux compiler: gcc - env: SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO + env: SHARED_BUILD=ON - os: linux compiler: gcc - env: SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO + env: SHARED_BUILD=ON - os: linux compiler: clang - env: TRAVIS_NO_EXPORT=NO ASAN=ON + env: ASAN=ON - os: linux compiler: clang - env: SHARED_BUILD=ON TRAVIS_NO_EXPORT=NO + env: SHARED_BUILD=ON install: - if [ $ANDROID ]; then wget -c http://dl.google.com/android/ndk/android-ndk-${PV}-${PLATF}.tar.bz2 && tar xf android-ndk-${PV}-${PLATF}.tar.bz2 ; fi From bd65811329b3685674127877fece1fa5e4648c55 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 7 Oct 2017 15:37:45 +0300 Subject: [PATCH 189/490] Travis: Rename TRAVIS_NO_EXPORT to DISABLE_EXPORTERS so its meaning is more obvious --- .travis.sh | 2 +- .travis.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.sh b/.travis.sh index 1c7c4e13e..f4ef271e4 100755 --- a/.travis.sh +++ b/.travis.sh @@ -2,7 +2,7 @@ function generate() { OPTIONS="-DASSIMP_WERROR=ON" - if [ "$TRAVIS_NO_EXPORT" = "YES" ] ; then + if [ "$DISABLE_EXPORTERS" = "YES" ] ; then OPTIONS="$OPTIONS -DASSIMP_NO_EXPORT=YES" else OPTIONS="$OPTIONS -DASSIMP_NO_EXPORT=NO" diff --git a/.travis.yml b/.travis.yml index 8c9736ef8..b43894525 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,7 +47,7 @@ matrix: include: - os: linux compiler: gcc - env: TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON + env: DISABLE_EXPORTERS=YES ENABLE_COVERALLS=ON - os: linux compiler: gcc env: SHARED_BUILD=ON From 684cb88e830c79c434fc472470af847ed43e1d9c Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 7 Oct 2017 15:39:54 +0300 Subject: [PATCH 190/490] Travis: Remove redundant config --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index b43894525..5f6d1a233 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,9 +51,6 @@ matrix: - os: linux compiler: gcc env: SHARED_BUILD=ON - - os: linux - compiler: gcc - env: SHARED_BUILD=ON - os: linux compiler: clang env: ASAN=ON From 10b49dfd2543404c7c0b6ceab6287b8ac2b73538 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 7 Oct 2017 15:41:16 +0300 Subject: [PATCH 191/490] Travis: Remove old attempt at disabling default configurations This didn't actually work and now the default configurations are important. --- .travis.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5f6d1a233..3ffa63176 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,14 +36,6 @@ env: - PV=r8e PLATF=linux-x86_64 NDK_HOME=${TRAVIS_BUILD_DIR}/android-ndk-${PV} PATH=${PATH}:${NDK_HOME} matrix: - exclude: - - os: linux - compiler: gcc - env: - - os: linux - compiler: clang - env: - include: - os: linux compiler: gcc From 8f5489243920f568d30c5dd629131fde3b02b651 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 01:27:21 +1100 Subject: [PATCH 192/490] Cleaned up appveyor setup, added VS 2017 to the build matrix and attempted to add running of tests. --- appveyor.yml | 49 ++++++++++++++++++----------- scripts/appveyor/compiler_setup.bat | 36 --------------------- 2 files changed, 30 insertions(+), 55 deletions(-) delete mode 100644 scripts/appveyor/compiler_setup.bat diff --git a/appveyor.yml b/appveyor.yml index b8828710b..5eabc8ada 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,39 +4,50 @@ # clone directory clone_folder: c:\projects\assimp +shallow_clone: true + # branches to build branches: # whitelist only: - master +image: + - Visual Studio 2013 + - Visual Studio 2015 + - Visual Studio 2017 + platform: - - x86 - - x64 + - x86 + - x64 -configuration: - - 14 2015 - - 12 2013 - #- MinGW - #- 10 2010 # only works for x86 +configuration: Release -init: -- if "%platform%" EQU "x64" ( for %%a in (2008 2010 MinGW) do ( if "%Configuration%"=="%%a" (echo "Skipping unsupported configuration" && exit /b 1 ) ) ) +build: + parallel: true + project: Assimp.sln install: -# Make compiler command line tools available -- call c:\projects\assimp\scripts\appveyor\compiler_setup.bat - -build_script: -- cd c:\projects\assimp -- if "%platform%" equ "x64" (cmake CMakeLists.txt -DASSIMP_WERROR=ON -G "Visual Studio %Configuration% Win64") -- if "%platform%" equ "x86" (cmake CMakeLists.txt -DASSIMP_WERROR=ON -G "Visual Studio %Configuration%") -- if "%platform%" equ "x64" (msbuild /m /p:Configuration=Release /p:Platform="x64" Assimp.sln) -- if "%platform%" equ "x86" (msbuild /m /p:Configuration=Release /p:Platform="Win32" Assimp.sln) + - @echo off + - set CMAKE_DEFINES -DASSIMP_WERROR=ON + - if "%platform%"=="x86" set CMAKE_GENERATOR_NAME=%APPVEYOR_BUILD_WORKER_IMAGE% + - if "%platform%"=="x64" set CMAKE_GENERATOR_NAME=%APPVEYOR_BUILD_WORKER_IMAGE% Win64 + - cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%" +cache: + - code\assimp.dir\%CONFIGURATION% + after_build: - - 7z a assimp.7z c:\projects\assimp\bin\release\* c:\projects\assimp\lib\release\* + - 7z a assimp.7z %APPVEYOR_BUILD_FOLDER%\bin\%CONFIGURATION%\* %APPVEYOR_BUILD_FOLDER%\lib\%CONFIGURATION%\* + +before_test: + - xcopy /s "%APPVEYOR_BUILD_FOLDER%\bin\%CONFIGURATION%\" "%APPVEYOR_BUILD_FOLDER%\test\%CONFIGURATION%\" +test: + assemblies: + only: + - test\%CONFIGURATION%\unit.exe + artifacts: - path: assimp.7z name: assimp_lib diff --git a/scripts/appveyor/compiler_setup.bat b/scripts/appveyor/compiler_setup.bat deleted file mode 100644 index 7d4851493..000000000 --- a/scripts/appveyor/compiler_setup.bat +++ /dev/null @@ -1,36 +0,0 @@ -rem @echo off - -:: Now we declare a scope -Setlocal EnableDelayedExpansion EnableExtensions - -if not defined Configuration set Configuration=14 2015 - -if "%Configuration%"=="MinGW" ( goto :mingw ) - -set arch=x86 - -if "%platform%" EQU "x64" ( set arch=x86_amd64 ) - -if "%Configuration%"=="14 2015" ( - set SET_VS_ENV="C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" -) - -if "%Configuration%"=="12 2013" ( - set SET_VS_ENV="C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" -) - -if "%Configuration%"=="11 2012" ( - set SET_VS_ENV="C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat" -) - -if "%Configuration%"=="10 2010" ( - set SET_VS_ENV="C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" -) - -:: Visual Studio detected -endlocal & call %SET_VS_ENV% %arch% -goto :eof - -:: MinGW detected -:mingw -endlocal & set PATH=c:\mingw\bin;%PATH% From 63764ae42a9d0e0229bf02bc0f2b9a720f3f2494 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 01:33:48 +1100 Subject: [PATCH 193/490] Apparently @ escaping batch commands doesn't work in this context. --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 5eabc8ada..b31137959 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -28,7 +28,7 @@ build: project: Assimp.sln install: - - @echo off + - echo off - set CMAKE_DEFINES -DASSIMP_WERROR=ON - if "%platform%"=="x86" set CMAKE_GENERATOR_NAME=%APPVEYOR_BUILD_WORKER_IMAGE% - if "%platform%"=="x64" set CMAKE_GENERATOR_NAME=%APPVEYOR_BUILD_WORKER_IMAGE% Win64 From 4c06abf281ef1eacaec42ca39b5437c7c511f430 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 01:47:08 +1100 Subject: [PATCH 194/490] Replaced the worker image name, which doesn't work as generator name, with a manually created generator name. --- appveyor.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index b31137959..653bcf937 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -28,10 +28,11 @@ build: project: Assimp.sln install: - - echo off - set CMAKE_DEFINES -DASSIMP_WERROR=ON - - if "%platform%"=="x86" set CMAKE_GENERATOR_NAME=%APPVEYOR_BUILD_WORKER_IMAGE% - - if "%platform%"=="x64" set CMAKE_GENERATOR_NAME=%APPVEYOR_BUILD_WORKER_IMAGE% Win64 + - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" set CMAKE_GENERATOR_NAME=Visual Studio 12 2013 + - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" set CMAKE_GENERATOR_NAME=Visual Studio 14 2015 + - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" set CMAKE_GENERATOR_NAME=Visual Studio 15 2017 + - if "%platform%"=="x64" set CMAKE_GENERATOR_NAME=%CMAKE_GENERATOR_NAME% Win64 - cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%" cache: From ba43e3a152f9dc8e7506c383019fdf7347c4369a Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 01:55:10 +1100 Subject: [PATCH 195/490] x86 isn't a valid VS platform. Win32 it is, then. --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 653bcf937..d43e8da45 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,7 +18,7 @@ image: - Visual Studio 2017 platform: - - x86 + - Win32 - x64 configuration: Release From d3de8dbf5ffcb430a1624cc9b607e2dcbffaea4a Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 02:03:00 +1100 Subject: [PATCH 196/490] Paths aren't what I expected for the test directory. Trying something else, with a testing call to dir to help track it down. --- appveyor.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index d43e8da45..8e35c0e1a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -42,12 +42,13 @@ after_build: - 7z a assimp.7z %APPVEYOR_BUILD_FOLDER%\bin\%CONFIGURATION%\* %APPVEYOR_BUILD_FOLDER%\lib\%CONFIGURATION%\* before_test: - - xcopy /s "%APPVEYOR_BUILD_FOLDER%\bin\%CONFIGURATION%\" "%APPVEYOR_BUILD_FOLDER%\test\%CONFIGURATION%\" + - dir "%APPVEYOR_BUILD_FOLDER%\bin" + - xcopy /s "%APPVEYOR_BUILD_FOLDER%\bin\%CONFIGURATION%" "%APPVEYOR_BUILD_FOLDER%\bin\test\%CONFIGURATION%" test: assemblies: only: - - test\%CONFIGURATION%\unit.exe + - bin\test\%CONFIGURATION%\unit.exe artifacts: - path: assimp.7z From fe79322959e3f6cdb4ab19f3bcc136a5dced1881 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 02:22:37 +1100 Subject: [PATCH 197/490] Attempting to get the tests detected and run. --- appveyor.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 8e35c0e1a..b461d9dda 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -40,15 +40,11 @@ cache: after_build: - 7z a assimp.7z %APPVEYOR_BUILD_FOLDER%\bin\%CONFIGURATION%\* %APPVEYOR_BUILD_FOLDER%\lib\%CONFIGURATION%\* - -before_test: - - dir "%APPVEYOR_BUILD_FOLDER%\bin" - - xcopy /s "%APPVEYOR_BUILD_FOLDER%\bin\%CONFIGURATION%" "%APPVEYOR_BUILD_FOLDER%\bin\test\%CONFIGURATION%" test: assemblies: only: - - bin\test\%CONFIGURATION%\unit.exe + - bin\%CONFIGURATION%\unit.exe artifacts: - path: assimp.7z From f925e2cf4e37cf8923bc2348e2c665379675a90e Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 7 Oct 2017 19:08:20 +0200 Subject: [PATCH 198/490] Reproduce issue and remove assertion when a nullptr makes more sence --- code/ObjFileParser.cpp | 8 -------- code/ValidateDataStructure.cpp | 4 +--- test/unit/utObjImportExport.cpp | 21 ++++++++++++--------- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/code/ObjFileParser.cpp b/code/ObjFileParser.cpp index 41677fce5..acf275b94 100644 --- a/code/ObjFileParser.cpp +++ b/code/ObjFileParser.cpp @@ -359,8 +359,6 @@ void ObjFileParser::getHomogeneousVector3( std::vector &point3d_arra m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } -// ------------------------------------------------------------------- -// Get values for two 3D vectors on the same line void ObjFileParser::getTwoVectors3( std::vector &point3d_array_a, std::vector &point3d_array_b ) { ai_real x, y, z; copyNextWord(m_buffer, Buffersize); @@ -388,8 +386,6 @@ void ObjFileParser::getTwoVectors3( std::vector &point3d_array_a, st m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } -// ------------------------------------------------------------------- -// Get values for a new 2D vector instance void ObjFileParser::getVector2( std::vector &point2d_array ) { ai_real x, y; copyNextWord(m_buffer, Buffersize); @@ -405,8 +401,6 @@ void ObjFileParser::getVector2( std::vector &point2d_array ) { static const std::string DefaultObjName = "defaultobject"; -// ------------------------------------------------------------------- -// Get values for a new face instance void ObjFileParser::getFace( aiPrimitiveType type ) { m_DataIt = getNextToken( m_DataIt, m_DataItEnd ); if ( m_DataIt == m_DataItEnd || *m_DataIt == '\0' ) { @@ -522,8 +516,6 @@ void ObjFileParser::getFace( aiPrimitiveType type ) { m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } -// ------------------------------------------------------------------- -// Get values for a new material description void ObjFileParser::getMaterialDesc() { // Get next data for material data m_DataIt = getNextToken(m_DataIt, m_DataItEnd); diff --git a/code/ValidateDataStructure.cpp b/code/ValidateDataStructure.cpp index ae1e0d342..f1035e441 100644 --- a/code/ValidateDataStructure.cpp +++ b/code/ValidateDataStructure.cpp @@ -89,9 +89,7 @@ AI_WONT_RETURN void ValidateDSProcess::ReportError(const char* msg,...) ai_assert(iLen > 0); va_end(args); -#ifdef ASSIMP_BUILD_DEBUG - ai_assert( false ); -#endif + throw DeadlyImportError("Validation failed: " + std::string(szBuffer,iLen)); } // ------------------------------------------------------------------------------------------------ diff --git a/test/unit/utObjImportExport.cpp b/test/unit/utObjImportExport.cpp index 94bc71f67..59fd58a6f 100644 --- a/test/unit/utObjImportExport.cpp +++ b/test/unit/utObjImportExport.cpp @@ -46,6 +46,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include +#include using namespace Assimp; @@ -257,15 +259,16 @@ TEST_F( utObjImportExport, issue809_vertex_color_Test ) { TEST_F( utObjImportExport, issue1453_segfault ) { static const std::string ObjModel = - "v 0.0 0.0 0.0" - "v 0.0 0.0 1.0" - "v 0.0 1.0 0.0" - "v 0.0 1.0 1.0" - "v 1.0 0.0 0.0" - "v 1.0 0.0 1.0" - "v 1.0 1.0 0.0" - "v 1.0 1.0 1.0"; + "v 0.0 0.0 0.0\n" + "v 0.0 0.0 1.0\n" + "v 0.0 1.0 0.0\n" + "v 0.0 1.0 1.0\n" + "v 1.0 0.0 0.0\n" + "v 1.0 0.0 1.0\n" + "v 1.0 1.0 0.0\n" + "v 1.0 1.0 1.0\nB"; Assimp::Importer myimporter; - const aiScene* myscene = myimporter.ReadFileFromMemory( ObjModel.c_str(), ObjModel.size(), 0 ); + const aiScene *scene = myimporter.ReadFileFromMemory( ObjModel.c_str(), ObjModel.size(), aiProcess_ValidateDataStructure ); + EXPECT_EQ( nullptr, scene ); } From 26851880e4a098370b9d65d9cad57154c38564a3 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 11:55:54 +1100 Subject: [PATCH 199/490] Attempting to address issues with cloning the repo. shallow_copy seems to fail at times. I'll let it continue to clone the whole history. --- appveyor.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index b461d9dda..380a8c577 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,8 +4,6 @@ # clone directory clone_folder: c:\projects\assimp -shallow_clone: true - # branches to build branches: # whitelist From 3e80aabde5cc27b2d02249894423d5edac763dd4 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 12:32:14 +1100 Subject: [PATCH 200/490] Attempting to get tests to run. --- appveyor.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 380a8c577..5a7694a1a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -39,10 +39,8 @@ cache: after_build: - 7z a assimp.7z %APPVEYOR_BUILD_FOLDER%\bin\%CONFIGURATION%\* %APPVEYOR_BUILD_FOLDER%\lib\%CONFIGURATION%\* -test: - assemblies: - only: - - bin\%CONFIGURATION%\unit.exe +test_script: + - cmd: bin\%CONFIGURATION%\unit.exe artifacts: - path: assimp.7z From 6a2dfb1efce678d11cf1f6524fabc1ec98ef3a83 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 12:44:54 +1100 Subject: [PATCH 201/490] Fixed up the path to the build artifacts to be cached. --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 5a7694a1a..6d5a046bf 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -34,7 +34,7 @@ install: - cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%" cache: - - code\assimp.dir\%CONFIGURATION% + - assimp.dir\%CONFIGURATION% after_build: - 7z a assimp.7z %APPVEYOR_BUILD_FOLDER%\bin\%CONFIGURATION%\* %APPVEYOR_BUILD_FOLDER%\lib\%CONFIGURATION%\* From 52da099738e3049ecfd85d5051a2d64c61d3a260 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 13:00:27 +1100 Subject: [PATCH 202/490] Updated test output to log to xml and to upload those results appropriately, so they show up in AppVeyor correctly as tests at the end. --- appveyor.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 6d5a046bf..26e72b3d0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -40,7 +40,10 @@ after_build: - 7z a assimp.7z %APPVEYOR_BUILD_FOLDER%\bin\%CONFIGURATION%\* %APPVEYOR_BUILD_FOLDER%\lib\%CONFIGURATION%\* test_script: - - cmd: bin\%CONFIGURATION%\unit.exe + - cmd: bin\%CONFIGURATION%\unit.exe -gtest_output=xml:testout.xml + +after_test: + - ps: (new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml)) artifacts: - path: assimp.7z From 81d3010f73dd86f7831fe4aabde5aa66ac96c3fb Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 16:21:32 +1100 Subject: [PATCH 203/490] Added the mtime_cache script to hopefully improve use of incremental building with appveyor. Reduced the verbosity of the appveyor config. Added use of mtime_cache. Fixed the output of an xml version of the test output for hooking into appveyor. --- appveyor.yml | 29 ++++-- scripts/AppVeyor/cacheglobs.txt | 4 + scripts/AppVeyor/mtime_cache | 177 ++++++++++++++++++++++++++++++++ 3 files changed, 203 insertions(+), 7 deletions(-) create mode 100644 scripts/AppVeyor/cacheglobs.txt create mode 100644 scripts/AppVeyor/mtime_cache diff --git a/appveyor.yml b/appveyor.yml index 26e72b3d0..67bf6cdec 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,28 +19,43 @@ platform: - Win32 - x64 +environment: + - build_cache_dir: bin\.mtime_cache + - build_binary_dir: bin\%CONFIGURATION% + - appveyor_script_dir: scripts\AppVeyor + configuration: Release -build: - parallel: true - project: Assimp.sln - install: + - set PATH=C:\Ruby24-x64\bin;%PATH% - set CMAKE_DEFINES -DASSIMP_WERROR=ON - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" set CMAKE_GENERATOR_NAME=Visual Studio 12 2013 - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" set CMAKE_GENERATOR_NAME=Visual Studio 14 2015 - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" set CMAKE_GENERATOR_NAME=Visual Studio 15 2017 - if "%platform%"=="x64" set CMAKE_GENERATOR_NAME=%CMAKE_GENERATOR_NAME% Win64 - cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%" - + cache: - assimp.dir\%CONFIGURATION% + - zlibstatic.dir\%CONFIGURATION% + - zlib.dir\%CONFIGURATION% + - assimp_cmd.dir\%CONFIGURATION% + - assimp_viewer.dir\%CONFIGURATION% + - unit.dir\%CONFIGURATION% + - $(build_cache_dir) + +before_build: + - ruby $(appveyor_script_dir)\mtime_cache -g $(appveyor_script_dir)\cacheglobs.txt -c $(build_cache_dir)\cache.json + +build: + parallel: true + project: Assimp.sln after_build: - - 7z a assimp.7z %APPVEYOR_BUILD_FOLDER%\bin\%CONFIGURATION%\* %APPVEYOR_BUILD_FOLDER%\lib\%CONFIGURATION%\* + - 7z a assimp.7z $(build_binary_dir)\* lib\%CONFIGURATION%\* test_script: - - cmd: bin\%CONFIGURATION%\unit.exe -gtest_output=xml:testout.xml + - cmd: $(build_binary_dir)\unit.exe --gtest_output=xml:testout.xml after_test: - ps: (new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml)) diff --git a/scripts/AppVeyor/cacheglobs.txt b/scripts/AppVeyor/cacheglobs.txt new file mode 100644 index 000000000..0f5f04a4a --- /dev/null +++ b/scripts/AppVeyor/cacheglobs.txt @@ -0,0 +1,4 @@ +code/*.{%{cpp}} +contrib/**/*.{%{cpp}} +include/**/*.{%{cpp}} +test/**/*.{%{cpp}} diff --git a/scripts/AppVeyor/mtime_cache b/scripts/AppVeyor/mtime_cache new file mode 100644 index 000000000..e296e3658 --- /dev/null +++ b/scripts/AppVeyor/mtime_cache @@ -0,0 +1,177 @@ +#!/usr/bin/env ruby + +# +# mtime_cache +# Copyright (c) 2016 Borislav Stanimirov +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# + +require 'digest/md5' +require 'json' +require 'fileutils' + +VERSION = "1.0.2" + +VERSION_TEXT = "mtime_cache v#{VERSION}" + +USAGE = <] [-g globfile] [-d] [-q|V] [-c cache] +ENDUSAGE + +HELP = < '.mtime_cache.json', :globs => [] } + +ARGV.each do |arg| + case arg + when '-g', '--globfile' then param_arg = :globfile + when '-h', '-?', '--help' then ARGS[:help] = true + when '-v', '--version' then ARGS[:ver] = true + when '-q', '--quiet' then ARGS[:quiet] = true + when '-V', '--verbose' then ARGS[:verbose] = true + when '-d', '--dryrun' then ARGS[:dry] = true + when '-c', '--cache' then param_arg = :cache + else + if param_arg + ARGS[param_arg] = arg + param_arg = nil + else + ARGS[:globs] << arg + end + end +end + +def log(text, level = 0) + return if ARGS[:quiet] + return if level > 0 && !ARGS[:verbose] + puts text +end + +if ARGS[:ver] || ARGS[:help] + log VERSION_TEXT + exit if ARGS[:ver] + log USAGE + log HELP + exit +end + +if ARGS[:globs].empty? && !ARGS[:globfile] + log 'Error: Missing globs' + log USAGE + exit 1 +end + +EXTENSION_PATTERNS = { + :cpp => "c,cc,cpp,cxx,h,hpp,hxx,inl,ipp,inc,ixx" +} + +cache_file = ARGS[:cache] + +cache = {} + +if File.file?(cache_file) + log "Found #{cache_file}" + cache = JSON.parse(File.read(cache_file)) + log "Read #{cache.length} entries" +else + log "#{cache_file} not found. A new one will be created" +end + +globs = ARGS[:globs].map { |g| g % EXTENSION_PATTERNS } + +globfile = ARGS[:globfile] +if globfile + File.open(globfile, 'r').each_line do |line| + line.strip! + next if line.empty? + globs << line % EXTENSION_PATTERNS + end +end + +if globs.empty? + log 'Error: No globs in globfile' + log USAGE + exit 1 +end + +files = {} +num_changed = 0 + +globs.each do |glob| + Dir[glob].each do |file| + next if !File.file?(file) + + mtime = File.mtime(file).to_i + hash = Digest::MD5.hexdigest(File.read(file)) + + cached = cache[file] + + if cached && cached['hash'] == hash && cached['mtime'] < mtime + mtime = cached['mtime'] + + log "mtime_cache: changing mtime of #{file} to #{mtime}", 1 + + File.utime(File.atime(file), Time.at(mtime), file) if !ARGS[:dry] + num_changed += 1 + else + log "mtime_cache: NOT changing mtime of #{file}", 1 + end + + files[file] = { 'mtime' => mtime, 'hash' => hash } + end +end + +log "Changed mtime of #{num_changed} of #{files.length} files" +log "Writing #{cache_file}" + +if !ARGS[:dry] + dirname = File.dirname(cache_file) + unless File.directory?(dirname) + FileUtils.mkdir_p(dirname) + end + File.open(cache_file, 'w').write(JSON.pretty_generate(files)) +end From 90c9884c5e41b5ba8c6bf001710f767e053d6c8b Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 16:24:05 +1100 Subject: [PATCH 204/490] The environment section uses different syntax for a list of vars. --- appveyor.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 67bf6cdec..92d851ed2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,9 +20,9 @@ platform: - x64 environment: - - build_cache_dir: bin\.mtime_cache - - build_binary_dir: bin\%CONFIGURATION% - - appveyor_script_dir: scripts\AppVeyor + build_cache_dir: bin\.mtime_cache + build_binary_dir: bin\%CONFIGURATION% + appveyor_script_dir: scripts\AppVeyor configuration: Release From 58ac747634a9f8b2997c141998272c5c5d5cf935 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 16:28:43 +1100 Subject: [PATCH 205/490] Fixed some bad usage of environment variables. --- appveyor.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 92d851ed2..6bddf4684 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -45,17 +45,17 @@ cache: - $(build_cache_dir) before_build: - - ruby $(appveyor_script_dir)\mtime_cache -g $(appveyor_script_dir)\cacheglobs.txt -c $(build_cache_dir)\cache.json + - ruby %appveyor_script_dir%\mtime_cache -g %appveyor_script_dir%\cacheglobs.txt -c %build_cache_dir%\cache.json build: parallel: true project: Assimp.sln after_build: - - 7z a assimp.7z $(build_binary_dir)\* lib\%CONFIGURATION%\* + - 7z a assimp.7z %build_binary_dir%\* lib\%CONFIGURATION%\* test_script: - - cmd: $(build_binary_dir)\unit.exe --gtest_output=xml:testout.xml + - cmd: %build_binary_dir%\unit.exe --gtest_output=xml:testout.xml after_test: - ps: (new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml)) From 5149149b0760501b0aa0731283ae1ca7af0ec54b Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 16:31:46 +1100 Subject: [PATCH 206/490] The environment section doesn't do what I thought it would. Duplication is bad, but seems neccessary. --- appveyor.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 6bddf4684..74bb5ce3e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,11 +18,6 @@ image: platform: - Win32 - x64 - -environment: - build_cache_dir: bin\.mtime_cache - build_binary_dir: bin\%CONFIGURATION% - appveyor_script_dir: scripts\AppVeyor configuration: Release @@ -42,20 +37,20 @@ cache: - assimp_cmd.dir\%CONFIGURATION% - assimp_viewer.dir\%CONFIGURATION% - unit.dir\%CONFIGURATION% - - $(build_cache_dir) + - bin\.mtime_cache before_build: - - ruby %appveyor_script_dir%\mtime_cache -g %appveyor_script_dir%\cacheglobs.txt -c %build_cache_dir%\cache.json + - ruby scripts\AppVeyor\mtime_cache -g scripts\AppVeyor\cacheglobs.txt -c bin\.mtime_cache\cache.json build: parallel: true project: Assimp.sln after_build: - - 7z a assimp.7z %build_binary_dir%\* lib\%CONFIGURATION%\* + - 7z a assimp.7z bin\%CONFIGURATION%\* lib\%CONFIGURATION%\* test_script: - - cmd: %build_binary_dir%\unit.exe --gtest_output=xml:testout.xml + - cmd: bin\%CONFIGURATION%\unit.exe --gtest_output=xml:testout.xml after_test: - ps: (new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml)) From 5e5b7f4f75122520c2605a7665ed2335e2c80f7e Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 17:06:23 +1100 Subject: [PATCH 207/490] Refined the appveyor config so that test output is reported even if tests fail. --- appveyor.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 74bb5ce3e..43f83a264 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -51,10 +51,8 @@ after_build: test_script: - cmd: bin\%CONFIGURATION%\unit.exe --gtest_output=xml:testout.xml - -after_test: - ps: (new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml)) - + artifacts: - path: assimp.7z name: assimp_lib From 54d2f25aa597790f47f24e92fdddedcd696aa25a Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 17:46:45 +1100 Subject: [PATCH 208/490] Attempting to get test results reporting even if an error occurrs. --- appveyor.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 43f83a264..e3485dd35 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -50,7 +50,9 @@ after_build: - 7z a assimp.7z bin\%CONFIGURATION%\* lib\%CONFIGURATION%\* test_script: - - cmd: bin\%CONFIGURATION%\unit.exe --gtest_output=xml:testout.xml + - cmd: bin\%CONFIGURATION%\unit.exe --gtest_output=xml:testout.xml || ( +Powershell.exe -executionpolicy remotesigned -Command "(new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml))" && exit 1 +) - ps: (new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml)) artifacts: From 022a34e54c275330f3616b1c8bd88966771c8a9f Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 17:50:11 +1100 Subject: [PATCH 209/490] Fixing multiline batch command weirdness. --- appveyor.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index e3485dd35..3e958b36c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -51,8 +51,7 @@ after_build: test_script: - cmd: bin\%CONFIGURATION%\unit.exe --gtest_output=xml:testout.xml || ( -Powershell.exe -executionpolicy remotesigned -Command "(new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml))" && exit 1 -) +Powershell.exe -executionpolicy remotesigned -Command "(new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml))" && exit 1) - ps: (new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml)) artifacts: From 27b6cc22dbf4120d89a1bfee74b5f1bae1479502 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 17:51:42 +1100 Subject: [PATCH 210/490] No fun/multiline allowed. --- appveyor.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 3e958b36c..804dab9d1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -50,8 +50,7 @@ after_build: - 7z a assimp.7z bin\%CONFIGURATION%\* lib\%CONFIGURATION%\* test_script: - - cmd: bin\%CONFIGURATION%\unit.exe --gtest_output=xml:testout.xml || ( -Powershell.exe -executionpolicy remotesigned -Command "(new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml))" && exit 1) + - cmd: bin\%CONFIGURATION%\unit.exe --gtest_output=xml:testout.xml || (Powershell.exe -executionpolicy remotesigned -Command "(new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml))" && exit 1) - ps: (new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml)) artifacts: From 61836080c53c64275098756ded69bd680cb23f3c Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 18:01:58 +1100 Subject: [PATCH 211/490] Quote escaping across nested batch and powershell hurts my brain. --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 804dab9d1..6fa9a9a13 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -50,7 +50,7 @@ after_build: - 7z a assimp.7z bin\%CONFIGURATION%\* lib\%CONFIGURATION%\* test_script: - - cmd: bin\%CONFIGURATION%\unit.exe --gtest_output=xml:testout.xml || (Powershell.exe -executionpolicy remotesigned -Command "(new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml))" && exit 1) + - cmd: bin\%CONFIGURATION%\unit.exe --gtest_output=xml:testout.xml || (Powershell.exe -executionpolicy remotesigned -Command "(new-object net.webclient).UploadFile(`\"https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)`\", (Resolve-Path .\testout.xml))" && exit 1) - ps: (new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml)) artifacts: From e60e396721fea1a42f030dacc6cba864a4656dae Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 18:42:56 +1100 Subject: [PATCH 212/490] Attempting to address failures with chained batch and powershell commands. --- appveyor.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 6fa9a9a13..dee27d967 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,6 +21,9 @@ platform: configuration: Release +matrix: + fast_finish: true + install: - set PATH=C:\Ruby24-x64\bin;%PATH% - set CMAKE_DEFINES -DASSIMP_WERROR=ON @@ -50,7 +53,7 @@ after_build: - 7z a assimp.7z bin\%CONFIGURATION%\* lib\%CONFIGURATION%\* test_script: - - cmd: bin\%CONFIGURATION%\unit.exe --gtest_output=xml:testout.xml || (Powershell.exe -executionpolicy remotesigned -Command "(new-object net.webclient).UploadFile(`\"https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)`\", (Resolve-Path .\testout.xml))" && exit 1) + - cmd: bin\%CONFIGURATION%\unit.exe --gtest_output=xml:testout.xml || (Powershell.exe -executionpolicy remotesigned -Command "& '(new-object net.webclient).UploadFile(\"https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)\", (Resolve-Path .\testout.xml))'" && exit 1) - ps: (new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml)) artifacts: From d8e3952b64d4de3f0353fdb72c62a18b4c6cce10 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 18:57:35 +1100 Subject: [PATCH 213/490] Merging the test_script with test reporting was a terrible idea in retrospect. on_finish should serve the purpose. --- appveyor.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index dee27d967..f008b3cf2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -53,9 +53,11 @@ after_build: - 7z a assimp.7z bin\%CONFIGURATION%\* lib\%CONFIGURATION%\* test_script: - - cmd: bin\%CONFIGURATION%\unit.exe --gtest_output=xml:testout.xml || (Powershell.exe -executionpolicy remotesigned -Command "& '(new-object net.webclient).UploadFile(\"https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)\", (Resolve-Path .\testout.xml))'" && exit 1) - - ps: (new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml)) + - cmd: bin\%CONFIGURATION%\unit.exe --gtest_output=xml:testout.xml +on_finish: + - ps: (new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml)) + artifacts: - path: assimp.7z name: assimp_lib From 3b4ffbc1b694af759b76ce6dda0187cf7a1dda56 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 19:16:03 +1100 Subject: [PATCH 214/490] Test failures are now getting properly reported. Turning off 'fast finish' to allow all tests to execute. --- appveyor.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index f008b3cf2..c343c0d21 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,9 +21,6 @@ platform: configuration: Release -matrix: - fast_finish: true - install: - set PATH=C:\Ruby24-x64\bin;%PATH% - set CMAKE_DEFINES -DASSIMP_WERROR=ON From 7e033c6cefe66b28a8cbb921130759505dec875c Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sun, 8 Oct 2017 12:45:02 +0300 Subject: [PATCH 215/490] FBX: Remove useless const qualifier from return value const qualifier on primitive return type does nothing. --- code/FBXDocument.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/FBXDocument.h b/code/FBXDocument.h index af89f53d5..a4e28b2aa 100644 --- a/code/FBXDocument.h +++ b/code/FBXDocument.h @@ -596,10 +596,10 @@ public: return textures[index]; } - const int textureCount() const { + int textureCount() const { return static_cast(textures.size()); } - const BlendMode GetBlendMode() const + BlendMode GetBlendMode() const { return blendMode; } @@ -647,7 +647,7 @@ public: return content; } - const uint32_t ContentLength() const { + uint32_t ContentLength() const { return contentLength; } From cd64eae59058ab00d3f031730c823ccb53fd5a29 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sun, 8 Oct 2017 12:45:48 +0300 Subject: [PATCH 216/490] GenericProperty: Remove useless const qualifier from return value --- code/GenericProperty.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/GenericProperty.h b/code/GenericProperty.h index 8632e7577..454f4952b 100644 --- a/code/GenericProperty.h +++ b/code/GenericProperty.h @@ -111,7 +111,7 @@ inline void SetGenericPropertyPtr(std::map< unsigned int, T* >& list, // ------------------------------------------------------------------------------------------------ template -inline const bool HasGenericProperty(const std::map< unsigned int, T >& list, +inline bool HasGenericProperty(const std::map< unsigned int, T >& list, const char* szName) { ai_assert(NULL != szName); From f90d874978f2fb0cafde4fff74225319a5dce823 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sun, 8 Oct 2017 12:48:45 +0300 Subject: [PATCH 217/490] Open3DGC: Remove redundant const qualifiers from return types --- contrib/Open3DGC/o3dgcAdjacencyInfo.h | 4 +-- contrib/Open3DGC/o3dgcBinaryStream.h | 6 ++-- contrib/Open3DGC/o3dgcDynamicVector.h | 8 +++--- contrib/Open3DGC/o3dgcFIFO.h | 4 +-- contrib/Open3DGC/o3dgcIndexedFaceSet.h | 32 ++++++++++----------- contrib/Open3DGC/o3dgcTriangleListEncoder.h | 8 +++--- contrib/Open3DGC/o3dgcVector.h | 4 +-- 7 files changed, 33 insertions(+), 33 deletions(-) diff --git a/contrib/Open3DGC/o3dgcAdjacencyInfo.h b/contrib/Open3DGC/o3dgcAdjacencyInfo.h index 6b53a242d..72fe3d4c6 100644 --- a/contrib/Open3DGC/o3dgcAdjacencyInfo.h +++ b/contrib/Open3DGC/o3dgcAdjacencyInfo.h @@ -140,8 +140,8 @@ namespace o3dgc { return End(element) - Begin(element); } - long * const GetNumNeighborsBuffer() { return m_numNeighbors;} - long * const GetNeighborsBuffer() { return m_neighbors;} + long * GetNumNeighborsBuffer() { return m_numNeighbors;} + long * GetNeighborsBuffer() { return m_neighbors;} private: long m_neighborsSize; // actual allocated size for m_neighbors diff --git a/contrib/Open3DGC/o3dgcBinaryStream.h b/contrib/Open3DGC/o3dgcBinaryStream.h index 9f4aefe55..19e3df973 100644 --- a/contrib/Open3DGC/o3dgcBinaryStream.h +++ b/contrib/Open3DGC/o3dgcBinaryStream.h @@ -395,15 +395,15 @@ namespace o3dgc { return m_stream.GetSize(); } - const unsigned char * const GetBuffer(unsigned long position) const + const unsigned char * GetBuffer(unsigned long position) const { return m_stream.GetBuffer() + position; } - unsigned char * const GetBuffer(unsigned long position) + unsigned char * GetBuffer(unsigned long position) { return (m_stream.GetBuffer() + position); } - unsigned char * const GetBuffer() + unsigned char * GetBuffer() { return m_stream.GetBuffer(); } diff --git a/contrib/Open3DGC/o3dgcDynamicVector.h b/contrib/Open3DGC/o3dgcDynamicVector.h index edc97d83c..aa7fb3142 100644 --- a/contrib/Open3DGC/o3dgcDynamicVector.h +++ b/contrib/Open3DGC/o3dgcDynamicVector.h @@ -48,10 +48,10 @@ namespace o3dgc unsigned long GetNVector() const { return m_num;} unsigned long GetDimVector() const { return m_dim;} unsigned long GetStride() const { return m_stride;} - const Real * const GetMin() const { return m_min;} - const Real * const GetMax() const { return m_max;} - const Real * const GetVectors() const { return m_vectors;} - Real * const GetVectors() { return m_vectors;} + const Real * GetMin() const { return m_min;} + const Real * GetMax() const { return m_max;} + const Real * GetVectors() const { return m_vectors;} + Real * GetVectors() { return m_vectors;} Real GetMin(unsigned long j) const { return m_min[j];} Real GetMax(unsigned long j) const { return m_max[j];} diff --git a/contrib/Open3DGC/o3dgcFIFO.h b/contrib/Open3DGC/o3dgcFIFO.h index 874c26475..4a5555f2a 100644 --- a/contrib/Open3DGC/o3dgcFIFO.h +++ b/contrib/Open3DGC/o3dgcFIFO.h @@ -81,8 +81,8 @@ namespace o3dgc m_end = 0; } } - const unsigned long GetSize() const { return m_size;}; - const unsigned long GetAllocatedSize() const { return m_allocated;}; + unsigned long GetSize() const { return m_size;}; + unsigned long GetAllocatedSize() const { return m_allocated;}; void Clear() { m_start = m_end = m_size = 0;}; private: diff --git a/contrib/Open3DGC/o3dgcIndexedFaceSet.h b/contrib/Open3DGC/o3dgcIndexedFaceSet.h index 4af9de437..adb8cb001 100644 --- a/contrib/Open3DGC/o3dgcIndexedFaceSet.h +++ b/contrib/Open3DGC/o3dgcIndexedFaceSet.h @@ -62,26 +62,26 @@ namespace o3dgc } unsigned long GetNumFloatAttributes() const { return m_numFloatAttributes;} unsigned long GetNumIntAttributes() const { return m_numIntAttributes ;} - const Real * const GetCoordMin () const { return m_coordMin;} - const Real * const GetCoordMax () const { return m_coordMax;} - const Real * const GetNormalMin () const { return m_normalMin;} - const Real * const GetNormalMax () const { return m_normalMax;} + const Real * GetCoordMin () const { return m_coordMin;} + const Real * GetCoordMax () const { return m_coordMax;} + const Real * GetNormalMin () const { return m_normalMin;} + const Real * GetNormalMax () const { return m_normalMax;} Real GetCoordMin (int j) const { return m_coordMin[j] ;} Real GetCoordMax (int j) const { return m_coordMax[j] ;} Real GetNormalMin (int j) const { return m_normalMin[j] ;} Real GetNormalMax (int j) const { return m_normalMax[j] ;} - const O3DGCIFSFloatAttributeType GetFloatAttributeType(unsigned long a) const + O3DGCIFSFloatAttributeType GetFloatAttributeType(unsigned long a) const { assert(a < O3DGC_SC3DMC_MAX_NUM_FLOAT_ATTRIBUTES); return m_typeFloatAttribute[a]; } - const O3DGCIFSIntAttributeType GetIntAttributeType(unsigned long a) const + O3DGCIFSIntAttributeType GetIntAttributeType(unsigned long a) const { assert(a < O3DGC_SC3DMC_MAX_NUM_INT_ATTRIBUTES); return m_typeIntAttribute[a]; } - const unsigned long GetFloatAttributeDim(unsigned long a) const + unsigned long GetFloatAttributeDim(unsigned long a) const { assert(a < O3DGC_SC3DMC_MAX_NUM_FLOAT_ATTRIBUTES); return m_dimFloatAttribute[a]; @@ -91,12 +91,12 @@ namespace o3dgc assert(a < O3DGC_SC3DMC_MAX_NUM_INT_ATTRIBUTES); return m_dimIntAttribute[a]; } - const Real * const GetFloatAttributeMin(unsigned long a) const + const Real * GetFloatAttributeMin(unsigned long a) const { assert(a < O3DGC_SC3DMC_MAX_NUM_FLOAT_ATTRIBUTES); return &(m_minFloatAttribute[a * O3DGC_SC3DMC_MAX_DIM_ATTRIBUTES]); } - const Real * const GetFloatAttributeMax(unsigned long a) const + const Real * GetFloatAttributeMax(unsigned long a) const { assert(a < O3DGC_SC3DMC_MAX_NUM_FLOAT_ATTRIBUTES); return &(m_maxFloatAttribute[a * O3DGC_SC3DMC_MAX_DIM_ATTRIBUTES]); @@ -118,17 +118,17 @@ namespace o3dgc bool GetSolid() const { return m_solid ;} bool GetConvex() const { return m_convex ;} bool GetIsTriangularMesh() const { return m_isTriangularMesh;} - const unsigned long * const GetIndexBufferID() const { return m_indexBufferID ;} - const T * const GetCoordIndex() const { return m_coordIndex;} - T * const GetCoordIndex() { return m_coordIndex;} - Real * const GetCoord() const { return m_coord ;} - Real * const GetNormal() const { return m_normal ;} - Real * const GetFloatAttribute(unsigned long a) const + const unsigned long * GetIndexBufferID() const { return m_indexBufferID ;} + const T * GetCoordIndex() const { return m_coordIndex;} + T * GetCoordIndex() { return m_coordIndex;} + Real * GetCoord() const { return m_coord ;} + Real * GetNormal() const { return m_normal ;} + Real * GetFloatAttribute(unsigned long a) const { assert(a < O3DGC_SC3DMC_MAX_NUM_FLOAT_ATTRIBUTES); return m_floatAttribute[a]; } - long * const GetIntAttribute(unsigned long a) const + long * GetIntAttribute(unsigned long a) const { assert(a < O3DGC_SC3DMC_MAX_NUM_INT_ATTRIBUTES); return m_intAttribute[a] ; diff --git a/contrib/Open3DGC/o3dgcTriangleListEncoder.h b/contrib/Open3DGC/o3dgcTriangleListEncoder.h index cf790ecc3..c09172273 100644 --- a/contrib/Open3DGC/o3dgcTriangleListEncoder.h +++ b/contrib/Open3DGC/o3dgcTriangleListEncoder.h @@ -50,10 +50,10 @@ namespace o3dgc BinaryStream & bstream); O3DGCStreamType GetStreamType() const { return m_streamType; } void SetStreamType(O3DGCStreamType streamType) { m_streamType = streamType; } - const long * const GetInvVMap() const { return m_invVMap;} - const long * const GetInvTMap() const { return m_invTMap;} - const long * const GetVMap() const { return m_vmap;} - const long * const GetTMap() const { return m_tmap;} + const long * GetInvVMap() const { return m_invVMap;} + const long * GetInvTMap() const { return m_invTMap;} + const long * GetVMap() const { return m_vmap;} + const long * GetTMap() const { return m_tmap;} const AdjacencyInfo & GetVertexToTriangle() const { return m_vertexToTriangle;} private: diff --git a/contrib/Open3DGC/o3dgcVector.h b/contrib/Open3DGC/o3dgcVector.h index e766e2b7f..08d3ed564 100644 --- a/contrib/Open3DGC/o3dgcVector.h +++ b/contrib/Open3DGC/o3dgcVector.h @@ -88,8 +88,8 @@ namespace o3dgc assert(m_size < m_allocated); m_buffer[m_size++] = value; } - const T * const GetBuffer() const { return m_buffer;}; - T * const GetBuffer() { return m_buffer;}; + const T * GetBuffer() const { return m_buffer;}; + T * GetBuffer() { return m_buffer;}; unsigned long GetSize() const { return m_size;}; void SetSize(unsigned long size) { From 4a915653f5949b2aa173d2bc3466cf84fa92db48 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sun, 8 Oct 2017 23:42:28 +1100 Subject: [PATCH 218/490] Fixed IOStream reporting a file size of 0 for files that have been written, but not yet been flushed to disk. --- test/unit/utDefaultIOStream.cpp | 5 +++++ test/unit/utIOStreamBuffer.cpp | 13 +++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/test/unit/utDefaultIOStream.cpp b/test/unit/utDefaultIOStream.cpp index c0d82b6dc..128e4e6ef 100644 --- a/test/unit/utDefaultIOStream.cpp +++ b/test/unit/utDefaultIOStream.cpp @@ -69,6 +69,11 @@ TEST_F( utDefaultIOStream, FileSizeTest ) { auto vflush = std::fflush( fs ); ASSERT_EQ(vflush, 0); + std::fclose(fs); + fs = std::fopen(fpath, "r"); + + ASSERT_NE(nullptr, fs); + TestDefaultIOStream myStream( fs, fpath); size_t size = myStream.FileSize(); EXPECT_EQ( size, dataSize); diff --git a/test/unit/utIOStreamBuffer.cpp b/test/unit/utIOStreamBuffer.cpp index a1b67da44..f53a9c9d5 100644 --- a/test/unit/utIOStreamBuffer.cpp +++ b/test/unit/utIOStreamBuffer.cpp @@ -90,7 +90,11 @@ TEST_F( IOStreamBufferTest, open_close_Test ) { auto written = std::fwrite( data, sizeof(*data), dataCount, fs ); EXPECT_NE( 0U, written ); - std::fflush( fs ); + auto flushResult = std::fflush( fs ); + ASSERT_EQ(0, flushResult); + std::fclose( fs ); + fs = std::fopen(fname, "r"); + ASSERT_NE(nullptr, fs); { TestDefaultIOStream myStream( fs, fname ); @@ -112,7 +116,12 @@ TEST_F( IOStreamBufferTest, readlineTest ) { auto written = std::fwrite( data, sizeof(*data), dataCount, fs ); EXPECT_NE( 0U, written ); - std::fflush( fs ); + + auto flushResult = std::fflush(fs); + ASSERT_EQ(0, flushResult); + std::fclose(fs); + fs = std::fopen(fname, "r"); + ASSERT_NE(nullptr, fs); const auto tCacheSize = 26u; From 7353d25c139e97f19ce0de433f883a0bd7807d76 Mon Sep 17 00:00:00 2001 From: Haik Lorenz Date: Mon, 9 Oct 2017 15:47:17 +0200 Subject: [PATCH 219/490] Prevent failing stringstream to crash the export process Text exporters are using string streams to hold the file content first and then write them to the file in a single pass. If for whatever reason the stream has the fail bit set, tellp() will return pos_type(-1), which in turn makes the subsequent write crash - at least on Windows systems. One reason for the stream being in fail state is when its size exceeds 2^31 bytes, even on 64-bit systems (i.e., when very large scenes get exported). The fix is checking the fail() before even opening the file. --- code/ColladaExporter.cpp | 4 ++++ code/ObjExporter.cpp | 4 ++++ code/PlyExporter.cpp | 4 ++++ code/STLExporter.cpp | 8 ++++++++ code/XFileExporter.cpp | 4 ++++ 5 files changed, 24 insertions(+) diff --git a/code/ColladaExporter.cpp b/code/ColladaExporter.cpp index 1bac8b7f6..8f2e4047b 100644 --- a/code/ColladaExporter.cpp +++ b/code/ColladaExporter.cpp @@ -72,6 +72,10 @@ void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* p // invoke the exporter ColladaExporter iDoTheExportThing( pScene, pIOSystem, path, file); + + if (iDoTheExportThing.mOutput.fail()) { + throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); + } // we're still here - export successfully completed. Write result to the given IOSYstem std::unique_ptr outfile (pIOSystem->Open(pFile,"wt")); diff --git a/code/ObjExporter.cpp b/code/ObjExporter.cpp index d10dfcd45..ea3e19cd9 100644 --- a/code/ObjExporter.cpp +++ b/code/ObjExporter.cpp @@ -61,6 +61,10 @@ void ExportSceneObj(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene // invoke the exporter ObjExporter exporter(pFile, pScene); + if (exporter.mOutput.fail() || exporter.mOutputMat.fail()) { + throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); + } + // we're still here - export successfully completed. Write both the main OBJ file and the material script { std::unique_ptr outfile (pIOSystem->Open(pFile,"wt")); diff --git a/code/PlyExporter.cpp b/code/PlyExporter.cpp index 1d14c9219..bd3c94830 100644 --- a/code/PlyExporter.cpp +++ b/code/PlyExporter.cpp @@ -69,6 +69,10 @@ void ExportScenePly(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene // invoke the exporter PlyExporter exporter(pFile, pScene); + if (exporter.mOutput.fail()) { + throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); + } + // we're still here - export successfully completed. Write the file. std::unique_ptr outfile (pIOSystem->Open(pFile,"wt")); if(outfile == NULL) { diff --git a/code/STLExporter.cpp b/code/STLExporter.cpp index 3905cbcaf..c9d554655 100644 --- a/code/STLExporter.cpp +++ b/code/STLExporter.cpp @@ -61,6 +61,10 @@ void ExportSceneSTL(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene // invoke the exporter STLExporter exporter(pFile, pScene); + if (exporter.mOutput.fail()) { + throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); + } + // we're still here - export successfully completed. Write the file. std::unique_ptr outfile (pIOSystem->Open(pFile,"wt")); if(outfile == NULL) { @@ -74,6 +78,10 @@ void ExportSceneSTLBinary(const char* pFile,IOSystem* pIOSystem, const aiScene* // invoke the exporter STLExporter exporter(pFile, pScene, true); + if (exporter.mOutput.fail()) { + throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); + } + // we're still here - export successfully completed. Write the file. std::unique_ptr outfile (pIOSystem->Open(pFile,"wb")); if(outfile == NULL) { diff --git a/code/XFileExporter.cpp b/code/XFileExporter.cpp index 30a0a21f8..1e77c8b81 100644 --- a/code/XFileExporter.cpp +++ b/code/XFileExporter.cpp @@ -78,6 +78,10 @@ void ExportSceneXFile(const char* pFile,IOSystem* pIOSystem, const aiScene* pSce // invoke the exporter XFileExporter iDoTheExportThing( pScene, pIOSystem, path, file, &props); + if (iDoTheExportThing.mOutput.fail()) { + throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); + } + // we're still here - export successfully completed. Write result to the given IOSYstem std::unique_ptr outfile (pIOSystem->Open(pFile,"wt")); if(outfile == NULL) { From b8ad03baa1f96b83357cc5f14de854cd0f079a0b Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Tue, 10 Oct 2017 19:45:57 +1100 Subject: [PATCH 220/490] Because I have to change a file somewhere to properly test the cache on AppVeyor, I've made some whitespace a bit better. --- code/LogAux.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/code/LogAux.h b/code/LogAux.h index 08b1b3c17..a46b9c6ad 100644 --- a/code/LogAux.h +++ b/code/LogAux.h @@ -60,34 +60,34 @@ public: // ------------------------------------------------------------------------------------------------ static void ThrowException(const std::string& msg) { - throw DeadlyImportError(Prefix()+msg); + throw DeadlyImportError(Prefix() + msg); } // ------------------------------------------------------------------------------------------------ static void LogWarn(const Formatter::format& message) { if (!DefaultLogger::isNullLogger()) { - DefaultLogger::get()->warn(Prefix() +(std::string)message); + DefaultLogger::get()->warn(Prefix() + (std::string)message); } } // ------------------------------------------------------------------------------------------------ static void LogError(const Formatter::format& message) { if (!DefaultLogger::isNullLogger()) { - DefaultLogger::get()->error(Prefix() +(std::string)message); + DefaultLogger::get()->error(Prefix() + (std::string)message); } } // ------------------------------------------------------------------------------------------------ static void LogInfo(const Formatter::format& message) { if (!DefaultLogger::isNullLogger()) { - DefaultLogger::get()->info(Prefix() +(std::string)message); + DefaultLogger::get()->info(Prefix() + (std::string)message); } } // ------------------------------------------------------------------------------------------------ static void LogDebug(const Formatter::format& message) { if (!DefaultLogger::isNullLogger()) { - DefaultLogger::get()->debug(Prefix() +(std::string)message); + DefaultLogger::get()->debug(Prefix() + (std::string)message); } } From bb173749c191412723c41870ba5c812124988e87 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Tue, 10 Oct 2017 20:11:21 +1100 Subject: [PATCH 221/490] Attempting to fix the directories being cached. --- appveyor.yml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index c343c0d21..03b0ce73f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,6 +10,9 @@ branches: only: - master +matrix: + fast_finish: true + image: - Visual Studio 2013 - Visual Studio 2015 @@ -31,12 +34,12 @@ install: - cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%" cache: - - assimp.dir\%CONFIGURATION% - - zlibstatic.dir\%CONFIGURATION% - - zlib.dir\%CONFIGURATION% - - assimp_cmd.dir\%CONFIGURATION% - - assimp_viewer.dir\%CONFIGURATION% - - unit.dir\%CONFIGURATION% + - bin\assimp.dir\%CONFIGURATION% + - bin\zlibstatic.dir\%CONFIGURATION% + - bin\zlib.dir\%CONFIGURATION% + - bin\assimp_cmd.dir\%CONFIGURATION% + - bin\assimp_viewer.dir\%CONFIGURATION% + - bin\unit.dir\%CONFIGURATION% - bin\.mtime_cache before_build: @@ -48,6 +51,9 @@ build: after_build: - 7z a assimp.7z bin\%CONFIGURATION%\* lib\%CONFIGURATION%\* + - cmd: dir + - cmd: dir bin\ + - cmd: exit 1 test_script: - cmd: bin\%CONFIGURATION%\unit.exe --gtest_output=xml:testout.xml From dce2be9e09cd8fd14a763f64a345515fdea4800a Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Tue, 10 Oct 2017 21:24:40 +1100 Subject: [PATCH 222/490] I think I've worked out where the obj's are located. We shall see if the cache picks them up. --- appveyor.yml | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 03b0ce73f..1b87286f6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -34,12 +34,12 @@ install: - cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%" cache: - - bin\assimp.dir\%CONFIGURATION% - - bin\zlibstatic.dir\%CONFIGURATION% - - bin\zlib.dir\%CONFIGURATION% - - bin\assimp_cmd.dir\%CONFIGURATION% - - bin\assimp_viewer.dir\%CONFIGURATION% - - bin\unit.dir\%CONFIGURATION% + - code\assimp.dir\%CONFIGURATION% + - contrib\zlib\zlibstatic.dir\%CONFIGURATION% + - contrib\zlib\zlib.dir\%CONFIGURATION% + - tools\assimp_cmd\assimp_cmd.dir\%CONFIGURATION% + - tools\assimp_view\assimp_viewer.dir\%CONFIGURATION% + - test\unit.dir\%CONFIGURATION% - bin\.mtime_cache before_build: @@ -51,9 +51,6 @@ build: after_build: - 7z a assimp.7z bin\%CONFIGURATION%\* lib\%CONFIGURATION%\* - - cmd: dir - - cmd: dir bin\ - - cmd: exit 1 test_script: - cmd: bin\%CONFIGURATION%\unit.exe --gtest_output=xml:testout.xml From 1497cc27b2769efe0e74a5c0e206a2a32fb43c58 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Tue, 10 Oct 2017 22:08:13 +1100 Subject: [PATCH 223/490] Cleaned up the whitespace again. Let's see how the cache does. --- code/LogAux.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/code/LogAux.h b/code/LogAux.h index a46b9c6ad..f2bc14d54 100644 --- a/code/LogAux.h +++ b/code/LogAux.h @@ -60,34 +60,34 @@ public: // ------------------------------------------------------------------------------------------------ static void ThrowException(const std::string& msg) { - throw DeadlyImportError(Prefix() + msg); + throw DeadlyImportError(Prefix()+msg); } // ------------------------------------------------------------------------------------------------ static void LogWarn(const Formatter::format& message) { if (!DefaultLogger::isNullLogger()) { - DefaultLogger::get()->warn(Prefix() + (std::string)message); + DefaultLogger::get()->warn(Prefix()+(std::string)message); } } // ------------------------------------------------------------------------------------------------ static void LogError(const Formatter::format& message) { if (!DefaultLogger::isNullLogger()) { - DefaultLogger::get()->error(Prefix() + (std::string)message); + DefaultLogger::get()->error(Prefix()+(std::string)message); } } // ------------------------------------------------------------------------------------------------ static void LogInfo(const Formatter::format& message) { if (!DefaultLogger::isNullLogger()) { - DefaultLogger::get()->info(Prefix() + (std::string)message); + DefaultLogger::get()->info(Prefix()+(std::string)message); } } // ------------------------------------------------------------------------------------------------ static void LogDebug(const Formatter::format& message) { if (!DefaultLogger::isNullLogger()) { - DefaultLogger::get()->debug(Prefix() + (std::string)message); + DefaultLogger::get()->debug(Prefix()+(std::string)message); } } From fa91a0f64c16f9f2afcf6b631302bf918527e2f7 Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Tue, 10 Oct 2017 22:45:45 +1100 Subject: [PATCH 224/490] Another minor source change, this time even more minor than the last. Let's see what the cache can do with this. --- code/PretransformVertices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/PretransformVertices.cpp b/code/PretransformVertices.cpp index 5fc294618..0751c4556 100644 --- a/code/PretransformVertices.cpp +++ b/code/PretransformVertices.cpp @@ -104,7 +104,7 @@ unsigned int PretransformVertices::CountNodes( aiNode* pcNode ) // ------------------------------------------------------------------------------------------------ // Get a bitwise combination identifying the vertex format of a mesh -unsigned int PretransformVertices::GetMeshVFormat(aiMesh* pcMesh) +unsigned int PretransformVertices::GetMeshVFormat( aiMesh* pcMesh ) { // the vertex format is stored in aiMesh::mBones for later retrieval. // there isn't a good reason to compute it a few hundred times From 8f141c1966121f9b57756d8c40bc73d0f94c8501 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 10 Oct 2017 18:56:37 +0200 Subject: [PATCH 225/490] Update utObjImportExport.cpp Remove unused header. --- test/unit/utObjImportExport.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/unit/utObjImportExport.cpp b/test/unit/utObjImportExport.cpp index 2fc4d0a31..4aafa3dc2 100644 --- a/test/unit/utObjImportExport.cpp +++ b/test/unit/utObjImportExport.cpp @@ -47,7 +47,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include using namespace Assimp; From 3ef1f37a809ee7876d1faee0262d0b72ae7f8315 Mon Sep 17 00:00:00 2001 From: Doug Stephen Date: Wed, 11 Oct 2017 10:54:27 -0500 Subject: [PATCH 226/490] Create AiMetadataEntry.java for jassimp port. --- .../jassimp/src/jassimp/AiMetadataEntry.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 port/jassimp/jassimp/src/jassimp/AiMetadataEntry.java diff --git a/port/jassimp/jassimp/src/jassimp/AiMetadataEntry.java b/port/jassimp/jassimp/src/jassimp/AiMetadataEntry.java new file mode 100644 index 000000000..3dd983b45 --- /dev/null +++ b/port/jassimp/jassimp/src/jassimp/AiMetadataEntry.java @@ -0,0 +1,35 @@ +package jassimp; + +/** + * @author Doug Stephen (dstephen@ihmc.us) + */ +public class AiMetadataEntry +{ + public enum AiMetadataType + { + AI_BOOL, AI_INT32, AI_UINT64, AI_FLOAT, AI_DOUBLE, AI_AISTRING, AI_AIVECTOR3D + } + + private AiMetadataType mType; + private Object mData; + + public AiMetadataType getMetaDataType() + { + return mType; + } + +// public void setMetaDataType(AiMetadataType type) +// { +// this.mType = type; +// } + + public Object getData() + { + return mData; + } + +// public void setData(Object data) +// { +// this.mData = data; +// } +} From 00eb2e401a8088af072cdcdcb2253242bf2c4386 Mon Sep 17 00:00:00 2001 From: Doug Stephen Date: Wed, 11 Oct 2017 10:56:40 -0500 Subject: [PATCH 227/490] Added field and getter for metadata entries to AiNode.java. --- port/jassimp/jassimp/src/jassimp/AiNode.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/port/jassimp/jassimp/src/jassimp/AiNode.java b/port/jassimp/jassimp/src/jassimp/AiNode.java index 5cd26b668..9345b7af4 100644 --- a/port/jassimp/jassimp/src/jassimp/AiNode.java +++ b/port/jassimp/jassimp/src/jassimp/AiNode.java @@ -41,7 +41,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package jassimp; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** @@ -185,6 +187,18 @@ public final class AiNode { public int[] getMeshes() { return m_meshReferences; } + + /** + * Returns the metadata entries for this node.

+ * + * Consult the original Doxygen for importer_notes to + * see which formats have metadata and what to expect. + * + * @return A map of metadata names to entries. + */ + public Map getMetadata() { + return m_metaData; + } /** @@ -219,6 +233,11 @@ public final class AiNode { * List of children. */ private final List m_children = new ArrayList(); + + /** + * List of metadata entries. + */ + private final Map m_metaData = new HashMap(); /** From 33a54f021e2ebc0ea199f50f203621f459ff5993 Mon Sep 17 00:00:00 2001 From: Doug Stephen Date: Wed, 11 Oct 2017 11:03:29 -0500 Subject: [PATCH 228/490] Fix small bug in getStaticField --- port/jassimp/jassimp-native/src/jassimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/jassimp/jassimp-native/src/jassimp.cpp b/port/jassimp/jassimp-native/src/jassimp.cpp index 75b1bc510..5dbcb22cd 100644 --- a/port/jassimp/jassimp-native/src/jassimp.cpp +++ b/port/jassimp/jassimp-native/src/jassimp.cpp @@ -214,7 +214,7 @@ static bool getStaticField(JNIEnv *env, const char* className, const char* field return false; } - jfieldID fieldId = env->GetFieldID(clazz, fieldName, signature); + jfieldID fieldId = env->GetStaticFieldID(clazz, fieldName, signature); if (NULL == fieldId) { From 0229a3acf359ccc1b8ee597a1f92541ba38cbcbe Mon Sep 17 00:00:00 2001 From: Jesper Smith Date: Thu, 27 Jul 2017 17:42:01 -0500 Subject: [PATCH 229/490] Added supported for custom IO Systems in Java. Implemented ClassLoader IO System --- port/jassimp/jassimp-native/src/jassimp.cpp | 258 +++++++++++++++++- port/jassimp/jassimp-native/src/jassimp.h | 2 +- .../src/jassimp/AiClassLoaderIOSystem.java | 130 +++++++++ .../jassimp/src/jassimp/AiIOStream.java | 55 ++++ .../jassimp/src/jassimp/AiIOSystem.java | 54 ++++ .../src/jassimp/AiInputStreamIOStream.java | 102 +++++++ port/jassimp/jassimp/src/jassimp/Jassimp.java | 36 ++- 7 files changed, 619 insertions(+), 18 deletions(-) create mode 100644 port/jassimp/jassimp/src/jassimp/AiClassLoaderIOSystem.java create mode 100644 port/jassimp/jassimp/src/jassimp/AiIOStream.java create mode 100644 port/jassimp/jassimp/src/jassimp/AiIOSystem.java create mode 100644 port/jassimp/jassimp/src/jassimp/AiInputStreamIOStream.java diff --git a/port/jassimp/jassimp-native/src/jassimp.cpp b/port/jassimp/jassimp-native/src/jassimp.cpp index 226e416aa..475f6c5a0 100644 --- a/port/jassimp/jassimp-native/src/jassimp.cpp +++ b/port/jassimp/jassimp-native/src/jassimp.cpp @@ -1,7 +1,9 @@ #include "jassimp.h" -#include +#include #include +#include +#include #ifdef JNI_LOG @@ -12,9 +14,11 @@ #define lprintf(...) printf (__VA_ARGS__) #endif /* ANDROID */ #else -#define lprintf +#define lprintf #endif +static std::string gLastErrorString; + // Automatically deletes a local ref when it goes out of scope class SmartLocalRef { private: @@ -270,6 +274,81 @@ static bool callv(JNIEnv *env, jobject object, const char* typeName, return true; } +static jobject callo(JNIEnv *env, jobject object, const char* typeName, const char* methodName, + const char* signature,/* const*/ jvalue* params) +{ + jclass clazz = env->FindClass(typeName); + SmartLocalRef clazzRef(env, clazz); + + if (NULL == clazz) + { + lprintf("could not find class %s\n", typeName); + return NULL; + } + + jmethodID mid = env->GetMethodID(clazz, methodName, signature); + + if (NULL == mid) + { + lprintf("could not find method %s with signature %s in type %s\n", methodName, signature, typeName); + return NULL; + } + + jobject jReturnValue = env->CallObjectMethodA(object, mid, params); + + return jReturnValue; +} + +static int calli(JNIEnv *env, jobject object, const char* typeName, const char* methodName, + const char* signature) +{ + jclass clazz = env->FindClass(typeName); + SmartLocalRef clazzRef(env, clazz); + + if (NULL == clazz) + { + lprintf("could not find class %s\n", typeName); + return false; + } + + jmethodID mid = env->GetMethodID(clazz, methodName, signature); + + if (NULL == mid) + { + lprintf("could not find method %s with signature %s in type %s\n", methodName, signature, typeName); + return false; + } + + jint jReturnValue = env->CallIntMethod(object, mid); + + return (int) jReturnValue; +} + +static int callc(JNIEnv *env, jobject object, const char* typeName, const char* methodName, + const char* signature) +{ + jclass clazz = env->FindClass(typeName); + SmartLocalRef clazzRef(env, clazz); + + if (NULL == clazz) + { + lprintf("could not find class %s\n", typeName); + return false; + } + + jmethodID mid = env->GetMethodID(clazz, methodName, signature); + + if (NULL == mid) + { + lprintf("could not find method %s with signature %s in type %s\n", methodName, signature, typeName); + return false; + } + + jint jReturnValue = env->CallCharMethod(object, mid); + + return (int) jReturnValue; +} + static bool callStaticObject(JNIEnv *env, const char* typeName, const char* methodName, const char* signature,/* const*/ jvalue* params, jobject& returnValue) @@ -359,6 +438,155 @@ static bool copyBufferArray(JNIEnv *env, jobject jMesh, const char* jBufferName, return true; } +class JavaIOStream : public Assimp::IOStream +{ +private: + size_t pos; + size_t size; + char* buffer; + jobject jIOStream; + + +public: + JavaIOStream(size_t size, char* buffer, jobject jIOStream) : + pos(0), + size(size), + buffer(buffer), + jIOStream(jIOStream) + {}; + + + ~JavaIOStream(void) + { + free(buffer); + }; + + size_t Read(void* pvBuffer, size_t pSize, size_t pCount) + { + const size_t cnt = std::min(pCount,(size - pos)/pSize); + const size_t ofs = pSize*cnt; + + memcpy(pvBuffer, buffer + pos, ofs); + pos += ofs; + + return cnt; + }; + size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) {}; + + aiReturn Seek(size_t pOffset, aiOrigin pOrigin) + { + if (aiOrigin_SET == pOrigin) { + if (pOffset >= size) { + return AI_FAILURE; + } + pos = pOffset; + } + else if (aiOrigin_END == pOrigin) { + if (pOffset >= size) { + return AI_FAILURE; + } + pos = size-pOffset; + } + else { + if (pOffset + pos >= size) { + return AI_FAILURE; + } + pos += pOffset; + } + return AI_SUCCESS; + }; + + size_t Tell(void) const + { + return pos; + }; + + size_t FileSize() const + { + return size; + }; + + void Flush() {}; + + + jobject javaObject() + { + return jIOStream; + }; + + +}; + + +class JavaIOSystem : public Assimp::IOSystem { + private: + JNIEnv* mJniEnv; + jobject& mJavaIOSystem; + + public: + JavaIOSystem(JNIEnv* env, jobject& javaIOSystem) : + mJniEnv(env), + mJavaIOSystem(javaIOSystem) + {}; + + bool Exists( const char* pFile) const + { + jvalue params[1]; + params[0].l = mJniEnv->NewStringUTF(pFile); + return call(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "exists", "(Ljava/lang/String;)Z", params); + + }; + char getOsSeparator() const + { + return (char) callc(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "getOsSeparator", "()C"); + }; + + Assimp::IOStream* Open(const char* pFile,const char* pMode = "rb") + { + jvalue params[2]; + params[0].l = mJniEnv->NewStringUTF(pFile); + params[1].l = mJniEnv->NewStringUTF(pMode); + + + jobject jStream = callo(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "open", "(Ljava/lang/String;Ljava/lang/String;)Ljassimp/AiIOStream;", params); + if(NULL == jStream) + { + lprintf("NULL object from AiIOSystem.open\n"); + return NULL; + } + + size_t size = calli(mJniEnv, jStream, "jassimp/AiIOStream", "getFileSize", "()I"); + lprintf("Model file size is %d\n", size); + + char* buffer = (char*)malloc(size); + jobject javaBuffer = mJniEnv->NewDirectByteBuffer(buffer, size); + + jvalue readParams[1]; + readParams[0].l = javaBuffer; + if(call(mJniEnv, jStream, "jassimp/AiIOStream", "read", "(Ljava/nio/ByteBuffer;)Z", readParams)) + { + return new JavaIOStream(size, buffer, jStream); + } + else + { + lprintf("Read failure on AiIOStream.read"); + free(buffer); + return NULL; + } + + }; + void Close( Assimp::IOStream* pFile) + { + + jvalue params[1]; + params[0].l = ((JavaIOStream*) pFile)->javaObject(); + callv(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "close", "(Ljassimp/AiIOStream;)V", params); + delete pFile; + }; + + + +}; static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) @@ -1474,7 +1702,7 @@ JNIEXPORT jint JNICALL Java_jassimp_Jassimp_getlongsize JNIEXPORT jstring JNICALL Java_jassimp_Jassimp_getErrorString (JNIEnv *env, jclass jClazz) { - const char *err = aiGetErrorString(); + const char *err = gLastErrorString.c_str(); if (NULL == err) { @@ -1486,18 +1714,26 @@ JNIEXPORT jstring JNICALL Java_jassimp_Jassimp_getErrorString JNIEXPORT jobject JNICALL Java_jassimp_Jassimp_aiImportFile - (JNIEnv *env, jclass jClazz, jstring jFilename, jlong postProcess) + (JNIEnv *env, jclass jClazz, jstring jFilename, jlong postProcess, jobject ioSystem) { jobject jScene = NULL; /* convert params */ const char* cFilename = env->GetStringUTFChars(jFilename, NULL); + + Assimp::Importer imp; - + + if(ioSystem != NULL) + { + imp.SetIOHandler(new JavaIOSystem(env, ioSystem)); + lprintf("Created aiFileIO\n"); + } + lprintf("opening file: %s\n", cFilename); /* do import */ - const aiScene *cScene = aiImportFile(cFilename, (unsigned int) postProcess); + const aiScene *cScene = imp.ReadFile(cFilename, (unsigned int) postProcess); if (!cScene) { @@ -1552,19 +1788,13 @@ error: /* thats really a problem because we cannot throw in this case */ env->FatalError("could not throw java.io.IOException"); } - - env->ThrowNew(exception, aiGetErrorString()); + gLastErrorString = imp.GetErrorString(); + env->ThrowNew(exception, gLastErrorString.c_str()); lprintf("problem detected\n"); } end: - /* - * NOTE: this releases all memory used in the native domain. - * Ensure all data has been passed to java before! - */ - aiReleaseImport(cScene); - /* free params */ env->ReleaseStringUTFChars(jFilename, cFilename); diff --git a/port/jassimp/jassimp-native/src/jassimp.h b/port/jassimp/jassimp-native/src/jassimp.h index f448dc2c2..7d4b66e29 100644 --- a/port/jassimp/jassimp-native/src/jassimp.h +++ b/port/jassimp/jassimp-native/src/jassimp.h @@ -39,7 +39,7 @@ JNIEXPORT jstring JNICALL Java_jassimp_Jassimp_getErrorString * Signature: (Ljava/lang/String;J)Ljassimp/AiScene; */ JNIEXPORT jobject JNICALL Java_jassimp_Jassimp_aiImportFile - (JNIEnv *, jclass, jstring, jlong); + (JNIEnv *, jclass, jstring, jlong, jobject); #ifdef __cplusplus } diff --git a/port/jassimp/jassimp/src/jassimp/AiClassLoaderIOSystem.java b/port/jassimp/jassimp/src/jassimp/AiClassLoaderIOSystem.java new file mode 100644 index 000000000..04d638610 --- /dev/null +++ b/port/jassimp/jassimp/src/jassimp/AiClassLoaderIOSystem.java @@ -0,0 +1,130 @@ +/* + * Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jassimp; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +/** + * IOSystem based on the Java classloader.

+ * + * This IOSystem allows loading models directly from the + * classpath. No extraction to the file system is + * necessary. + * + * @author Jesper Smith + * + */ +public class AiClassLoaderIOSystem implements AiIOSystem +{ + private final Class clazz; + private final ClassLoader classLoader; + + /** + * Construct a new AiClassLoaderIOSystem.

+ * + * This constructor uses a ClassLoader to resolve + * resources. + * + * @param classLoader classLoader to resolve resources. + */ + public AiClassLoaderIOSystem(ClassLoader classLoader) { + this.clazz = null; + this.classLoader = classLoader; + } + + /** + * Construct a new AiClassLoaderIOSystem.

+ * + * This constructor uses a Class to resolve + * resources. + * + * @param class class to resolve resources. + */ + public AiClassLoaderIOSystem(Class clazz) { + this.clazz = clazz; + this.classLoader = null; + } + + + @Override + public AiInputStreamIOStream open(String filename, String ioMode) { + try { + + InputStream is; + + if(clazz != null) { + is = clazz.getResourceAsStream(filename); + } + else if (classLoader != null) { + is = classLoader.getResourceAsStream(filename); + } + else { + System.err.println("[" + getClass().getSimpleName() + + "] No class or classLoader provided to resolve " + filename); + return null; + } + + if(is != null) { + return new AiInputStreamIOStream(is); + } + else { + System.err.println("[" + getClass().getSimpleName() + + "] Cannot find " + filename); + return null; + } + } + catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + @Override + public void close(AiInputStreamIOStream file) { + } + + @Override + public boolean exists(String path) + { + URL url = null; + if(clazz != null) { + url = clazz.getResource(path); + } + else if (classLoader != null) { + url = classLoader.getResource(path); + } + + + if(url == null) + { + return false; + } + else + { + return true; + } + + } + + @Override + public char getOsSeparator() + { + return '/'; + } + +} diff --git a/port/jassimp/jassimp/src/jassimp/AiIOStream.java b/port/jassimp/jassimp/src/jassimp/AiIOStream.java new file mode 100644 index 000000000..5378da5f8 --- /dev/null +++ b/port/jassimp/jassimp/src/jassimp/AiIOStream.java @@ -0,0 +1,55 @@ +/* + * Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jassimp; + +import java.nio.ByteBuffer; + + +/** + * Interface to allow custom resource loaders for jassimp.

+ * + * The design is based on passing the file wholly in memory, + * because Java inputstreams do not have to support seek.

+ * + * Writing files from Java is unsupported. + * + * + * @author Jesper Smith + * + */ +public interface AiIOStream +{ + + /** + * Read all data into buffer.

+ * + * The whole stream should be read into the buffer. + * No support is provided for partial reads. + * + * @param buffer Target buffer for the model data + * + * @return true if successful, false if an error occurred. + */ + boolean read(ByteBuffer buffer); + + /** + * The total size of this stream.

+ * + * @return total size of this stream + */ + int getFileSize(); + +} diff --git a/port/jassimp/jassimp/src/jassimp/AiIOSystem.java b/port/jassimp/jassimp/src/jassimp/AiIOSystem.java new file mode 100644 index 000000000..d2c741529 --- /dev/null +++ b/port/jassimp/jassimp/src/jassimp/AiIOSystem.java @@ -0,0 +1,54 @@ +/* + * Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jassimp; + +public interface AiIOSystem +{ + /** + * + * Open a new file with a given path. + * When the access to the file is finished, call close() to release all associated resources + * + * @param path Path to the file + * @param ioMode file I/O mode. Required are: "wb", "w", "wt", "rb", "r", "rt". + * + * @return AiIOStream or null if an error occurred + */ + public T open(String path, String ioMode); + + + /** + * Tests for the existence of a file at the given path. + * + * @param path path to the file + * @return true if there is a file with this path, else false. + */ + public boolean exists(String path); + + /** + * Returns the system specific directory separator.

+ * + * @return System specific directory separator + */ + public char getOsSeparator(); + + /** + * Closes the given file and releases all resources associated with it. + * + * @param file The file instance previously created by Open(). + */ + public void close(T file); +} diff --git a/port/jassimp/jassimp/src/jassimp/AiInputStreamIOStream.java b/port/jassimp/jassimp/src/jassimp/AiInputStreamIOStream.java new file mode 100644 index 000000000..998401b68 --- /dev/null +++ b/port/jassimp/jassimp/src/jassimp/AiInputStreamIOStream.java @@ -0,0 +1,102 @@ +/* + * Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jassimp; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.net.URL; +import java.nio.ByteBuffer; + + +/** + * Implementation of AiIOStream reading from a InputStream + * + * @author Jesper Smith + * + */ +public class AiInputStreamIOStream implements AiIOStream +{ + private final ByteArrayOutputStream os = new ByteArrayOutputStream(); + + + public AiInputStreamIOStream(URI uri) throws IOException { + this(uri.toURL()); + } + + public AiInputStreamIOStream(URL url) throws IOException { + this(url.openStream()); + } + + public AiInputStreamIOStream(InputStream is) throws IOException { + int read; + byte[] data = new byte[1024]; + while((read = is.read(data, 0, data.length)) != -1) { + os.write(data, 0, read); + } + os.flush(); + + is.close(); + } + + @Override + public int getFileSize() { + return os.size(); + } + + @Override + public boolean read(ByteBuffer buffer) { + ByteBufferOutputStream bos = new ByteBufferOutputStream(buffer); + try + { + os.writeTo(bos); + } + catch (IOException e) + { + e.printStackTrace(); + return false; + } + return true; + } + + /** + * Internal helper class to copy the contents of an OutputStream + * into a ByteBuffer. This avoids a copy. + * + */ + private static class ByteBufferOutputStream extends OutputStream { + + private final ByteBuffer buffer; + + public ByteBufferOutputStream(ByteBuffer buffer) { + this.buffer = buffer; + } + + @Override + public void write(int b) throws IOException + { + buffer.put((byte) b); + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + buffer.put(b, off, len); + } + } +} + diff --git a/port/jassimp/jassimp/src/jassimp/Jassimp.java b/port/jassimp/jassimp/src/jassimp/Jassimp.java index 92f4864c7..d1b4aae4e 100644 --- a/port/jassimp/jassimp/src/jassimp/Jassimp.java +++ b/port/jassimp/jassimp/src/jassimp/Jassimp.java @@ -79,6 +79,20 @@ public final class Jassimp { return importFile(filename, EnumSet.noneOf(AiPostProcessSteps.class)); } + /** + * Imports a file via assimp without post processing. + * + * @param filename the file to import + * @param ioSystem ioSystem to load files, or null for default + * @return the loaded scene + * @throws IOException if an error occurs + */ + public static AiScene importFile(String filename, AiIOSystem ioSystem) + throws IOException { + + return importFile(filename, EnumSet.noneOf(AiPostProcessSteps.class), ioSystem); + } + /** * Imports a file via assimp. @@ -89,12 +103,28 @@ public final class Jassimp { * @throws IOException if an error occurs */ public static AiScene importFile(String filename, - Set postProcessing) throws IOException { + Set postProcessing) + throws IOException { + return importFile(filename, postProcessing, null); + } + + /** + * Imports a file via assimp. + * + * @param filename the file to import + * @param postProcessing post processing flags + * @param ioSystem ioSystem to load files, or null for default + * @return the loaded scene, or null if an error occurred + * @throws IOException if an error occurs + */ + public static AiScene importFile(String filename, + Set postProcessing, AiIOSystem ioSystem) + throws IOException { loadLibrary(); return aiImportFile(filename, AiPostProcessSteps.toRawValue( - postProcessing)); + postProcessing), ioSystem); } @@ -310,7 +340,7 @@ public final class Jassimp { * @throws IOException if an error occurs */ private static native AiScene aiImportFile(String filename, - long postProcessing) throws IOException; + long postProcessing, AiIOSystem ioSystem) throws IOException; /** From a7c1dde56e3ae4af5eaef801e938ee9e2265ecfb Mon Sep 17 00:00:00 2001 From: Jesper Smith Date: Fri, 28 Jul 2017 15:08:59 -0500 Subject: [PATCH 230/490] Added return statement to Write --- port/jassimp/jassimp-native/src/jassimp.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/port/jassimp/jassimp-native/src/jassimp.cpp b/port/jassimp/jassimp-native/src/jassimp.cpp index 475f6c5a0..75b1bc510 100644 --- a/port/jassimp/jassimp-native/src/jassimp.cpp +++ b/port/jassimp/jassimp-native/src/jassimp.cpp @@ -471,7 +471,10 @@ public: return cnt; }; - size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) {}; + size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) + { + return 0; + }; aiReturn Seek(size_t pOffset, aiOrigin pOrigin) { From 5e00d4d5cba4c444b609f23fa18b60b3ca067ba6 Mon Sep 17 00:00:00 2001 From: Doug Stephen Date: Wed, 11 Oct 2017 11:07:49 -0500 Subject: [PATCH 231/490] Populate metadata on Java objects. --- port/jassimp/jassimp-native/src/jassimp.cpp | 179 +++++++++++++++++++- 1 file changed, 171 insertions(+), 8 deletions(-) diff --git a/port/jassimp/jassimp-native/src/jassimp.cpp b/port/jassimp/jassimp-native/src/jassimp.cpp index 5dbcb22cd..c2a893896 100644 --- a/port/jassimp/jassimp-native/src/jassimp.cpp +++ b/port/jassimp/jassimp-native/src/jassimp.cpp @@ -1005,8 +1005,164 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) return true; } +static bool loadMetadata(JNIEnv *env, const aiNode* cNode, jobject& jNode) +{ + aiMetadata *cMetadata = cNode->mMetaData; -static bool loadSceneNode(JNIEnv *env, const aiNode *cNode, jobject parent, jobject* loadedNode = NULL) + for(unsigned i = 0; imNumProperties; i++) { + + aiString& metaDataKey = cMetadata->mKeys[i]; + void* cData = cMetadata->mValues[i].mData; + aiMetadataType cMetadataType = cMetadata->mValues[i].mType; + + jobject jAiMetadataEntry = NULL; + SmartLocalRef refMetadataEntry(env, jAiMetadataEntry); + + if(!createInstance(env, "jassimp/AiMetadataEntry", jAiMetadataEntry)) { + return false; + } + + jobject jAiMetadataTypeEnumValue = NULL; + SmartLocalRef refMetadataTypeEnumValue(env, jAiMetadataTypeEnumValue); + + jobject jMetadataData = NULL; + SmartLocalRef refMetadataData(env, jMetadataData); + + bool getMetadataTypeSuccess = false; + bool getMetadataDataSuccess = false; + + jvalue boxingMethodArgument[1]; + + jboolean exceptionThrown; + + switch (cMetadataType) { + + case AI_BOOL: { + getMetadataTypeSuccess = getStaticField(env, "jassimp/AiMetadataEntry$AiMetadataType", "AI_BOOL", "Ljassimp/AiMetadataEntry$AiMetadataType;", jAiMetadataTypeEnumValue); + boxingMethodArgument[0].z = (jboolean) *static_cast(cData); + getMetadataDataSuccess = callStaticObject(env, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", boxingMethodArgument, jMetadataData); + break; + } + case AI_INT32: { + getMetadataTypeSuccess = getStaticField(env, "jassimp/AiMetadataEntry$AiMetadataType", "AI_INT32", "Ljassimp/AiMetadataEntry$AiMetadataType;", jAiMetadataTypeEnumValue); + boxingMethodArgument[0].i = (jint) *static_cast(cData); + getMetadataDataSuccess = callStaticObject(env, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", boxingMethodArgument, jMetadataData); + break; + } + case AI_UINT64: { + getMetadataTypeSuccess = getStaticField(env, "jassimp/AiMetadataEntry$AiMetadataType", "AI_UINT64", "Ljassimp/AiMetadataEntry$AiMetadataType;", jAiMetadataTypeEnumValue); + boxingMethodArgument[0].j = (jlong) *static_cast(cData); + getMetadataDataSuccess = callStaticObject(env, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", boxingMethodArgument, jMetadataData); + break; + } + case AI_FLOAT: { + getMetadataTypeSuccess = getStaticField(env, "jassimp/AiMetadataEntry$AiMetadataType", "AI_FLOAT", "Ljassimp/AiMetadataEntry$AiMetadataType;", jAiMetadataTypeEnumValue); + boxingMethodArgument[0].f = (jfloat) *static_cast(cData); + getMetadataDataSuccess = callStaticObject(env, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", boxingMethodArgument, jMetadataData); + break; + } + case AI_DOUBLE: { + getMetadataTypeSuccess = getStaticField(env, "jassimp/AiMetadataEntry$AiMetadataType", "AI_DOUBLE", "Ljassimp/AiMetadataEntry$AiMetadataType;", jAiMetadataTypeEnumValue); + boxingMethodArgument[0].d = (jdouble) *static_cast(cData); + getMetadataDataSuccess = callStaticObject(env, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", boxingMethodArgument, jMetadataData); + break; + } + case AI_AISTRING: { + getMetadataTypeSuccess = getStaticField(env, "jassimp/AiMetadataEntry$AiMetadataType", "AI_AISTRING", "Ljassimp/AiMetadataEntry$AiMetadataType;", jAiMetadataTypeEnumValue); + jMetadataData = env->NewStringUTF(static_cast(cData)->C_Str()); + getMetadataDataSuccess = (jMetadataData != NULL); + break; + } + case AI_AIVECTOR3D: { + getMetadataTypeSuccess = getStaticField(env, "jassimp/AiMetadataEntry$AiMetadataType", "AI_AIVECTOR3D", + "Ljassimp/AiMetadataEntry$AiMetadataType;", + jAiMetadataTypeEnumValue); + jvalue wrapVec3Args[3]; + aiVector3D *vector3D = static_cast(cData); + wrapVec3Args[0].f = vector3D->x; + wrapVec3Args[1].f = vector3D->y; + wrapVec3Args[2].f = vector3D->z; + getMetadataDataSuccess = callStaticObject(env, "jassimp/Jassimp", "wrapVec3", "(FFF)Ljava/lang/Object;", + wrapVec3Args, jMetadataData); + break; + } + default: { + getMetadataTypeSuccess = false; + getMetadataDataSuccess = false; + break; + } + } + + exceptionThrown = env->ExceptionCheck(); + + if(!getMetadataTypeSuccess || !getMetadataDataSuccess) { + if(exceptionThrown) + { + env->ExceptionDescribe(); + } + + return false; + } + + if(!setObjectField(env, jAiMetadataEntry, "mType", "Ljassimp/AiMetadataEntry$AiMetadataType;", jAiMetadataTypeEnumValue)) { + exceptionThrown = env->ExceptionCheck(); + + if(exceptionThrown) + { + env->ExceptionDescribe(); + } + + return false; + } + + if(!setObjectField(env, jAiMetadataEntry, "mData", "Ljava/lang/Object;", jMetadataData)) { + exceptionThrown = env->ExceptionCheck(); + + if(exceptionThrown) + { + env->ExceptionDescribe(); + } + + return false; + } + + jobject jNodeMetadata = NULL; + SmartLocalRef refMetadata(env, jNodeMetadata); + + if(!getField(env, jNode, "m_metaData", "Ljava/util/Map;", jNodeMetadata)) { + exceptionThrown = env->ExceptionCheck(); + + if(exceptionThrown) + { + env->ExceptionDescribe(); + } + + return false; + } + + jclass hashMapClass = env->FindClass("java/util/HashMap"); + jmethodID jHashMapPutMethod = env->GetMethodID(hashMapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + + jstring jKey = env->NewStringUTF(metaDataKey.C_Str()); + SmartLocalRef keyRef(env, jKey); + + // Only check exception instead of result here because maps will return + // null on success if they did not overwrite an existing mapping for the given key. + env->CallObjectMethod(jNodeMetadata, jHashMapPutMethod, jKey, jAiMetadataEntry); + + exceptionThrown = env->ExceptionCheck(); + + if(exceptionThrown) { + env->ExceptionDescribe(); + return false; + } + + } + + return true; +} + +static bool loadSceneNode(JNIEnv *env, const aiNode *cNode, jobject parent, jobject* loadedNode = NULL) { lprintf(" converting node %s ...\n", cNode->mName.C_Str()); @@ -1019,7 +1175,7 @@ static bool loadSceneNode(JNIEnv *env, const aiNode *cNode, jobject parent, jobj wrapMatParams[0].l = jMatrixArr; jobject jMatrix; SmartLocalRef refMatrix(env, jMatrix); - + if (!callStaticObject(env, "jassimp/Jassimp", "wrapMatrix", "([F)Ljava/lang/Object;", wrapMatParams, jMatrix)) { return false; @@ -1068,12 +1224,19 @@ static bool loadSceneNode(JNIEnv *env, const aiNode *cNode, jobject parent, jobj } } - if (NULL != loadedNode) - { - *loadedNode = jNode; - } else { - env->DeleteLocalRef(jNode); - } + if (NULL != loadedNode) + { + if(cNode->mMetaData) { + if(!loadMetadata(env, cNode, jNode)) + { + return false; + } + } + + *loadedNode = jNode; + } else { + env->DeleteLocalRef(jNode); + } return true; } From e662f2dc6f5e073a8ae31e9d9b68d4b41267cdee Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 13 Oct 2017 22:41:38 +0200 Subject: [PATCH 232/490] Blender: fix short overflow. --- code/BlenderDNA.inl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/code/BlenderDNA.inl b/code/BlenderDNA.inl index 6919c9153..8b669180c 100644 --- a/code/BlenderDNA.inl +++ b/code/BlenderDNA.inl @@ -589,7 +589,10 @@ template <> inline void Structure :: Convert (short& dest,const FileData { // automatic rescaling from short to float and vice versa (seems to be used by normals) if (name == "float") { - dest = static_cast(db.reader->GetF4() * 32767.f); + float f = db.reader->GetF4(); + if ( f > 1.0f ) + f = 1.0f; + dest = static_cast( f * 32767.f); //db.reader->IncPtr(-4); return; } From f56e28ea3c55bbc74d0e9a808104bda9b39b2bbc Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 14 Oct 2017 16:40:39 +0200 Subject: [PATCH 233/490] UnitTest: use correct initialized normals in unittest. --- test/unit/utPretransformVertices.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/test/unit/utPretransformVertices.cpp b/test/unit/utPretransformVertices.cpp index 9f40f3ddc..437cbdba5 100644 --- a/test/unit/utPretransformVertices.cpp +++ b/test/unit/utPretransformVertices.cpp @@ -90,28 +90,36 @@ void PretransformVerticesTest::SetUp() // add 5 empty materials scene->mMaterials = new aiMaterial*[scene->mNumMaterials = 5]; - for (unsigned int i = 0; i < 5;++i) + for (unsigned int i = 0; i < 5;++i) { scene->mMaterials[i] = new aiMaterial(); + } // add 25 test meshes scene->mMeshes = new aiMesh*[scene->mNumMeshes = 25]; - for (unsigned int i = 0; i < 25;++i) { - aiMesh* mesh = scene->mMeshes[i] = new aiMesh(); + for ( unsigned int i = 0; i < 25; ++i) { + aiMesh* mesh = scene->mMeshes[ i ] = new aiMesh(); mesh->mPrimitiveTypes = aiPrimitiveType_POINT; mesh->mFaces = new aiFace[ mesh->mNumFaces = 10+i ]; mesh->mVertices = new aiVector3D[mesh->mNumVertices = mesh->mNumFaces]; for (unsigned int a = 0; a < mesh->mNumFaces; ++a ) { - aiFace& f = mesh->mFaces[a]; - f.mIndices = new unsigned int [f.mNumIndices = 1]; + aiFace& f = mesh->mFaces[ a ]; + f.mIndices = new unsigned int [ f.mNumIndices = 1 ]; f.mIndices[0] = a*3; mesh->mVertices[a] = aiVector3D((float)i,(float)a,0.f); } mesh->mMaterialIndex = i%5; - if (i % 2) + if (i % 2) { mesh->mNormals = new aiVector3D[mesh->mNumVertices]; + for ( unsigned int normalIdx=0; normalIdxmNumVertices; ++normalIdx ) { + mesh->mNormals[ normalIdx ].x = 1.0f; + mesh->mNormals[ normalIdx ].y = 1.0f; + mesh->mNormals[ normalIdx ].z = 1.0f; + mesh->mNormals[ normalIdx ].Normalize(); + } + } } // construct some nodes (1+25) From 6efe4e2841d710a7b3db44a9264fc8c2f52f1990 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 7 Oct 2017 17:13:08 +0300 Subject: [PATCH 234/490] CMake: Add support for Undefined Behavior sanitizer --- CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c30278b7f..bc3731bbb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,6 +86,10 @@ OPTION ( ASSIMP_ASAN "Enable AddressSanitizer." OFF ) +OPTION ( ASSIMP_UBSAN + "Enable Undefined Behavior sanitizer." + OFF +) OPTION ( SYSTEM_IRRXML "Use system installed Irrlicht/IrrXML library." OFF @@ -234,6 +238,12 @@ if (ASSIMP_ASAN) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") endif() +if (ASSIMP_UBSAN) + MESSAGE(STATUS "Undefined Behavior sanitizer enabled") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all") +endif() + INCLUDE (FindPkgMacros) INCLUDE (PrecompiledHeader) From 7b73fe8b026ec2dd55fb88a7fe8eccd971f0aad5 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 7 Oct 2017 17:47:06 +0300 Subject: [PATCH 235/490] Travis: Add Clang UBSan build configuration --- .travis.sh | 4 ++++ .travis.yml | 3 +++ 2 files changed, 7 insertions(+) diff --git a/.travis.sh b/.travis.sh index f4ef271e4..9786c5321 100755 --- a/.travis.sh +++ b/.travis.sh @@ -26,6 +26,10 @@ function generate() OPTIONS="$OPTIONS -DASSIMP_ASAN=OFF" fi + if [ "$UBSAN" = "ON" ] ; then + OPTIONS="$OPTIONS -DASSIMP_UBSAN=ON" + fi + cmake -G "Unix Makefiles" $OPTIONS } diff --git a/.travis.yml b/.travis.yml index 3ffa63176..fa37e5955 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,6 +46,9 @@ matrix: - os: linux compiler: clang env: ASAN=ON + - os: linux + compiler: clang + env: UBSAN=ON - os: linux compiler: clang env: SHARED_BUILD=ON From 7cbb5f4d3b7772861044876d570d142f9fc510cc Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 7 Oct 2017 18:36:09 +0300 Subject: [PATCH 236/490] B3DImporter: Replace bad pointer casting with memcpy --- code/B3DImporter.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/code/B3DImporter.cpp b/code/B3DImporter.cpp index d15676128..bc888fb66 100644 --- a/code/B3DImporter.cpp +++ b/code/B3DImporter.cpp @@ -171,7 +171,8 @@ int B3DImporter::ReadByte(){ // ------------------------------------------------------------------------------------------------ int B3DImporter::ReadInt(){ if( _pos+4<=_buf.size() ){ - int n=*(int*)&_buf[_pos]; + int n; + memcpy(&n, &_buf[_pos], 4); _pos+=4; return n; } @@ -182,7 +183,8 @@ int B3DImporter::ReadInt(){ // ------------------------------------------------------------------------------------------------ float B3DImporter::ReadFloat(){ if( _pos+4<=_buf.size() ){ - float n=*(float*)&_buf[_pos]; + float n; + memcpy(&n, &_buf[_pos], 4); _pos+=4; return n; } From 9a6b141568c33c01afd9aeac0a283a72dffcf496 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 7 Oct 2017 20:40:35 +0300 Subject: [PATCH 237/490] FBX: Replace bad pointer casting with memcpy --- code/FBXBinaryTokenizer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/FBXBinaryTokenizer.cpp b/code/FBXBinaryTokenizer.cpp index ede32d7b7..9ae38386b 100644 --- a/code/FBXBinaryTokenizer.cpp +++ b/code/FBXBinaryTokenizer.cpp @@ -151,7 +151,8 @@ uint32_t ReadWord(const char* input, const char*& cursor, const char* end) TokenizeError("cannot ReadWord, out of bounds",input, cursor); } - uint32_t word = *reinterpret_cast(cursor); + uint32_t word; + memcpy(&word, cursor, 4); AI_SWAP4(word); cursor += k_to_read; From f8c40022941178a58deb8161eef493e98fcb6bed Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sat, 14 Oct 2017 22:45:00 +1100 Subject: [PATCH 238/490] Fixed a divide by zero error in IFCBoolean that was latent, but nevertheless a bug --- code/IFCBoolean.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/IFCBoolean.cpp b/code/IFCBoolean.cpp index 8571a3c79..d250fbe36 100644 --- a/code/IFCBoolean.cpp +++ b/code/IFCBoolean.cpp @@ -272,7 +272,6 @@ bool IntersectsBoundaryProfile(const IfcVector3& e0, const IfcVector3& e1, const const IfcVector3& b0 = boundary[i]; const IfcVector3& b1 = boundary[(i + 1) % bcount]; IfcVector3 b = b1 - b0; - IfcFloat b_sqlen_inv = 1.0 / b.SquareLength(); // segment-segment intersection // solve b0 + b*s = e0 + e*t for (s,t) @@ -281,6 +280,7 @@ bool IntersectsBoundaryProfile(const IfcVector3& e0, const IfcVector3& e1, const // no solutions (parallel lines) continue; } + IfcFloat b_sqlen_inv = 1.0 / b.SquareLength(); const IfcFloat x = b0.x - e0.x; const IfcFloat y = b0.y - e0.y; From b2eb599176dfa5e98285e992422515455ccf137a Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 16 Oct 2017 18:51:25 +0200 Subject: [PATCH 239/490] Update ColladaExporter.cpp Retrigger travis. --- code/ColladaExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ColladaExporter.cpp b/code/ColladaExporter.cpp index 691ef9500..53ee07388 100644 --- a/code/ColladaExporter.cpp +++ b/code/ColladaExporter.cpp @@ -108,7 +108,7 @@ ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, co // set up strings endstr = "\n"; - // start writing + // start writing the file WriteFile(); } From 82debbf54a700d83dc969fe14d37eca19ebe04a2 Mon Sep 17 00:00:00 2001 From: Jesper Smith Date: Mon, 16 Oct 2017 12:41:50 -0500 Subject: [PATCH 240/490] Fixed copyright notice for IHMC jassimp improvements to BSD. Updated README --- port/jassimp/README | 1 - .../src/jassimp/AiClassLoaderIOSystem.java | 53 ++++++++++++++----- .../jassimp/src/jassimp/AiIOStream.java | 53 ++++++++++++++----- .../jassimp/src/jassimp/AiIOSystem.java | 53 ++++++++++++++----- .../src/jassimp/AiInputStreamIOStream.java | 53 ++++++++++++++----- 5 files changed, 156 insertions(+), 57 deletions(-) diff --git a/port/jassimp/README b/port/jassimp/README index 1ce9acc2b..a642f750a 100644 --- a/port/jassimp/README +++ b/port/jassimp/README @@ -44,7 +44,6 @@ jassimp supports most of assimps features. Current limitations are * no support for mesh animations * no support for embedded textures * no support for importer configurations -* no support for the custom I/O API of assimp * some texture related material properties are not exposed via the API but only accessible by traversing the list of properties. However this limitation is also present in the c-API ... diff --git a/port/jassimp/jassimp/src/jassimp/AiClassLoaderIOSystem.java b/port/jassimp/jassimp/src/jassimp/AiClassLoaderIOSystem.java index 04d638610..e14a87f77 100644 --- a/port/jassimp/jassimp/src/jassimp/AiClassLoaderIOSystem.java +++ b/port/jassimp/jassimp/src/jassimp/AiClassLoaderIOSystem.java @@ -1,18 +1,43 @@ /* - * Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +--------------------------------------------------------------------------- +Open Asset Import Library - Java Binding (jassimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ package jassimp; import java.io.IOException; diff --git a/port/jassimp/jassimp/src/jassimp/AiIOStream.java b/port/jassimp/jassimp/src/jassimp/AiIOStream.java index 5378da5f8..6625b3740 100644 --- a/port/jassimp/jassimp/src/jassimp/AiIOStream.java +++ b/port/jassimp/jassimp/src/jassimp/AiIOStream.java @@ -1,18 +1,43 @@ /* - * Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +--------------------------------------------------------------------------- +Open Asset Import Library - Java Binding (jassimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ package jassimp; import java.nio.ByteBuffer; diff --git a/port/jassimp/jassimp/src/jassimp/AiIOSystem.java b/port/jassimp/jassimp/src/jassimp/AiIOSystem.java index d2c741529..213f95a12 100644 --- a/port/jassimp/jassimp/src/jassimp/AiIOSystem.java +++ b/port/jassimp/jassimp/src/jassimp/AiIOSystem.java @@ -1,18 +1,43 @@ /* - * Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +--------------------------------------------------------------------------- +Open Asset Import Library - Java Binding (jassimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ package jassimp; public interface AiIOSystem diff --git a/port/jassimp/jassimp/src/jassimp/AiInputStreamIOStream.java b/port/jassimp/jassimp/src/jassimp/AiInputStreamIOStream.java index 998401b68..0db1ea211 100644 --- a/port/jassimp/jassimp/src/jassimp/AiInputStreamIOStream.java +++ b/port/jassimp/jassimp/src/jassimp/AiInputStreamIOStream.java @@ -1,18 +1,43 @@ /* - * Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +--------------------------------------------------------------------------- +Open Asset Import Library - Java Binding (jassimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ package jassimp; import java.io.ByteArrayOutputStream; From ff758e4c155770a866405e4802b5e84aab97251c Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Sun, 15 Oct 2017 19:53:26 +0200 Subject: [PATCH 241/490] OBJ : add unit test to validate relative indices --- test/unit/utObjImportExport.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/unit/utObjImportExport.cpp b/test/unit/utObjImportExport.cpp index 4aafa3dc2..3b08df80f 100644 --- a/test/unit/utObjImportExport.cpp +++ b/test/unit/utObjImportExport.cpp @@ -280,3 +280,28 @@ TEST_F( utObjImportExport, issue1453_segfault ) { const aiScene *scene = myimporter.ReadFileFromMemory( ObjModel.c_str(), ObjModel.size(), aiProcess_ValidateDataStructure ); EXPECT_EQ( nullptr, scene ); } + +TEST_F(utObjImportExport, relative_indices_Test) { + static const std::string ObjModel = + "v -0.500000 0.000000 0.400000\n" + "v -0.500000 0.000000 -0.800000\n" + "v -0.500000 1.000000 -0.800000\n" + "v -0.500000 1.000000 0.400000\n" + "f -4 -3 -2 -1\nB"; + + Assimp::Importer myimporter; + const aiScene *scene = myimporter.ReadFileFromMemory(ObjModel.c_str(), ObjModel.size(), aiProcess_ValidateDataStructure); + EXPECT_NE(nullptr, scene); + + EXPECT_EQ(scene->mNumMeshes, 1); + const aiMesh *mesh = scene->mMeshes[0]; + EXPECT_EQ(mesh->mNumVertices, 4); + EXPECT_EQ(mesh->mNumFaces, 1); + const aiFace face = mesh->mFaces[0]; + EXPECT_EQ(face.mNumIndices, 4); + for (unsigned int i = 0; i < face.mNumIndices; ++i) + { + EXPECT_EQ(face.mIndices[i], i); + } + +} From 1aa15c806955749311ec5e8cffc39ff20dd86282 Mon Sep 17 00:00:00 2001 From: Doug Stephen Date: Tue, 17 Oct 2017 10:35:50 -0500 Subject: [PATCH 242/490] Fix header and remove old debug code --- .../jassimp/src/jassimp/AiMetadataEntry.java | 52 ++++++++++++++----- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/port/jassimp/jassimp/src/jassimp/AiMetadataEntry.java b/port/jassimp/jassimp/src/jassimp/AiMetadataEntry.java index 3dd983b45..615572e79 100644 --- a/port/jassimp/jassimp/src/jassimp/AiMetadataEntry.java +++ b/port/jassimp/jassimp/src/jassimp/AiMetadataEntry.java @@ -1,8 +1,44 @@ package jassimp; +/* +--------------------------------------------------------------------------- +Open Asset Import Library - Java Binding (jassimp) +--------------------------------------------------------------------------- -/** - * @author Doug Stephen (dstephen@ihmc.us) - */ +Copyright (c) 2006-2012, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ public class AiMetadataEntry { public enum AiMetadataType @@ -18,18 +54,8 @@ public class AiMetadataEntry return mType; } -// public void setMetaDataType(AiMetadataType type) -// { -// this.mType = type; -// } - public Object getData() { return mData; } - -// public void setData(Object data) -// { -// this.mData = data; -// } } From 42e2c30b4b5cff63eae028a0e889a9ae7a831180 Mon Sep 17 00:00:00 2001 From: Doug Stephen Date: Tue, 17 Oct 2017 11:04:51 -0500 Subject: [PATCH 243/490] Added helper getters for casting metadata payloads --- .../jassimp/src/jassimp/AiMetadataEntry.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/port/jassimp/jassimp/src/jassimp/AiMetadataEntry.java b/port/jassimp/jassimp/src/jassimp/AiMetadataEntry.java index 615572e79..dbdf1aae8 100644 --- a/port/jassimp/jassimp/src/jassimp/AiMetadataEntry.java +++ b/port/jassimp/jassimp/src/jassimp/AiMetadataEntry.java @@ -58,4 +58,61 @@ public class AiMetadataEntry { return mData; } + + public static boolean getAiBoolAsBoolean(AiMetadataEntry metadataEntry) + { + checkTypeBeforeCasting(metadataEntry, AiMetadataType.AI_BOOL); + + return (boolean) metadataEntry.mData; + } + + public static int getAiInt32AsInteger(AiMetadataEntry metadataEntry) + { + checkTypeBeforeCasting(metadataEntry, AiMetadataType.AI_INT32); + + return (int) metadataEntry.mData; + } + + public static long getAiUint64AsLong(AiMetadataEntry metadataEntry) + { + checkTypeBeforeCasting(metadataEntry, AiMetadataType.AI_UINT64); + + return (long) metadataEntry.mData; + } + + public static float getAiFloatAsFloat(AiMetadataEntry metadataEntry) + { + checkTypeBeforeCasting(metadataEntry, AiMetadataType.AI_FLOAT); + + return (float) metadataEntry.mData; + } + + public static double getAiDoubleAsDouble(AiMetadataEntry metadataEntry) + { + checkTypeBeforeCasting(metadataEntry, AiMetadataType.AI_DOUBLE); + + return (double) metadataEntry.mData; + } + + public static String getAiStringAsString(AiMetadataEntry metadataEntry) + { + checkTypeBeforeCasting(metadataEntry, AiMetadataType.AI_AISTRING); + + return (String) metadataEntry.mData; + } + + public static AiVector getAiAiVector3DAsAiVector(AiMetadataEntry metadataEntry) + { + checkTypeBeforeCasting(metadataEntry, AiMetadataType.AI_AIVECTOR3D); + + return (AiVector) metadataEntry.mData; + } + + private static void checkTypeBeforeCasting(AiMetadataEntry entry, AiMetadataType expectedType) + { + if(entry.mType != expectedType) + { + throw new RuntimeException("Cannot cast entry of type " + entry.mType.name() + " to " + expectedType.name()); + } + } } From 4354cce313548c686fd0331dbf6fb22837b97aad Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 18 Oct 2017 15:06:34 +0200 Subject: [PATCH 244/490] Update Readme.md Fix typo. --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 1eaa6b8e1..a7187246b 100644 --- a/Readme.md +++ b/Readme.md @@ -119,7 +119,7 @@ Take a look into the `INSTALL` file. Our build system is CMake, if you used CMak * [.NET](port/AssimpNET/Readme.md) * [Pascal](port/AssimpPascal/Readme.md) * [Javascript (Alpha)](https://github.com/makc/assimp2json) -* [Unity 3d Plugin] (https://www.assetstore.unity3d.com/en/#!/content/91777) +* [Unity 3d Plugin](https://www.assetstore.unity3d.com/en/#!/content/91777) * [JVM](https://github.com/kotlin-graphics/assimp) Full jvm port (currently supported obj, ply, stl, ~collada) ### Other tools ### From 299c34f0631bbd3fc79c0b066c81b781f211a5e0 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 19 Oct 2017 09:43:56 +0200 Subject: [PATCH 245/490] Update .travis.sh Remove dead code. --- .travis.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.sh b/.travis.sh index 9786c5321..b074f9af5 100755 --- a/.travis.sh +++ b/.travis.sh @@ -32,7 +32,7 @@ function generate() cmake -G "Unix Makefiles" $OPTIONS } - +# build and run unittests, if not android if [ $ANDROID ]; then ant -v -Dmy.dir=${TRAVIS_BUILD_DIR} -f ${TRAVIS_BUILD_DIR}/port/jassimp/build.xml ndk-jni fi @@ -41,7 +41,5 @@ if [ "$TRAVIS_OS_NAME" = "linux" ]; then && make -j4 \ && sudo make install \ && sudo ldconfig \ - && (cd test/unit; ../../bin/unit) \ - #&& (cd test/regression; chmod 755 run.py; ./run.py ../../bin/assimp; \ - # chmod 755 result_checker.py; ./result_checker.py) + && (cd test/unit; ../../bin/unit) fi From 832c1fcc7fa70113406e9e6e0e675688138dadb6 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 19 Oct 2017 09:55:15 +0200 Subject: [PATCH 246/490] Update .travis.sh Add license info --- .travis.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.travis.sh b/.travis.sh index b074f9af5..24aff9368 100755 --- a/.travis.sh +++ b/.travis.sh @@ -1,3 +1,10 @@ +#--------------------------------------------------------------------------- +#Open Asset Import Library (assimp) +#--------------------------------------------------------------------------- +# Copyright (c) 2006-2017, assimp team +# +# License see LICENSE file +# function generate() { OPTIONS="-DASSIMP_WERROR=ON" From 5eaf083fbd1313431d28ea4fcac4e9775b289003 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Thu, 19 Oct 2017 12:38:11 -0400 Subject: [PATCH 247/490] Fix output of glTF 1 version string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Was writing out “\u0001†instead of “1.0†as the data types were incorrect --- code/glTFExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/glTFExporter.cpp b/code/glTFExporter.cpp index 92abcc15c..39886aaad 100644 --- a/code/glTFExporter.cpp +++ b/code/glTFExporter.cpp @@ -834,7 +834,7 @@ void glTFExporter::ExportScene() void glTFExporter::ExportMetadata() { glTF::AssetMetadata& asset = mAsset->asset; - asset.version = 1; + asset.version = "1.0"; char buffer[256]; ai_snprintf(buffer, 256, "Open Asset Import Library (assimp v%d.%d.%d)", From c71790c78da53d3b5c5e9ef6bd25b0c2aff59b98 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Thu, 19 Oct 2017 12:01:40 -0400 Subject: [PATCH 248/490] Diffuse color and diffuse texture import and export improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These changes do a better of importing and exporting baseColor colors and textures, as well as diffuse colors and textures (in the case of pbrSpecularGlossiness) - baseColorFactor will be stored on both `$clr.diffuse` and `$mat.gltf.pbrMetallicRoughness.baseColorFactor`, and will be extracted from `$mat.gltf.pbrMetallicRoughness.baseColorFactor` first, and falling back to `$clr.diffuse`. The behaviour for baseColorTexture is similar - pbrSG’s diffuseFactor will now only be store on `$clr.diffuse` (overwriting any previous assignments to `$clr.diffuse`, e.g. from metallicRoughness’ baseColorFactor, as diffuseFactor is more analogous to diffuse color than baseColor), and will only extract from `$clr.diffuse` --- code/glTF2Asset.h | 4 ++-- code/glTF2Exporter.cpp | 35 +++++++++++++++++++++++++++-------- code/glTF2Exporter.h | 4 ++-- code/glTF2Importer.cpp | 8 ++++++-- 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 63282dc6e..84211aeb6 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -164,16 +164,16 @@ namespace glTF2 //! Magic number for GLB files #define AI_GLB_MAGIC_NUMBER "glTF" + #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0 #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0 + #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE aiTextureType_DIFFUSE, 1 #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 0 #define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0 #define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0 #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 - #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_FACTOR "$clr.diffuse", 0, 1 #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULAR_FACTOR "$clr.specular", 0, 1 #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 - #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_TEXTURE aiTextureType_DIFFUSE, 1 #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULARGLOSSINESS_TEXTURE aiTextureType_UNKNOWN, 1 #define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 8f46f7dc7..26ff77913 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -372,20 +372,28 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, OcclusionTextureInfo& prop, } } -void glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const char* propName, int type, int idx) +aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const char* propName, int type, int idx) { aiColor4D col; - if (mat->Get(propName, type, idx, col) == AI_SUCCESS) { + aiReturn result = mat->Get(propName, type, idx, col); + + if (result == AI_SUCCESS) { prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; prop[3] = col.a; } + + return result; } -void glTF2Exporter::GetMatColor(const aiMaterial* mat, vec3& prop, const char* propName, int type, int idx) +aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec3& prop, const char* propName, int type, int idx) { aiColor3D col; - if (mat->Get(propName, type, idx, col) == AI_SUCCESS) { + aiReturn result = mat->Get(propName, type, idx, col); + + if (result == AI_SUCCESS) { prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; } + + return result; } void glTF2Exporter::ExportMaterials() @@ -406,9 +414,20 @@ void glTF2Exporter::ExportMaterials() m->name = name; - GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_DIFFUSE); + GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE); + + if (!m->pbrMetallicRoughness.baseColorTexture.texture) { + //if there wasn't a baseColorTexture defined in the source, fallback to any diffuse texture + GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_DIFFUSE); + } + GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); - GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); + + if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR) != AI_SUCCESS) { + // if baseColorFactor wasn't defined, then the source is likely not a metallic roughness material. + //a fallback to any diffuse color should be used instead + GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); + } if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { //if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0 @@ -451,11 +470,11 @@ void glTF2Exporter::ExportMaterials() PbrSpecularGlossiness pbrSG; - GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_FACTOR); GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULAR_FACTOR); mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR, pbrSG.glossinessFactor); - GetMatTex(mat, pbrSG.diffuseTexture, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_TEXTURE); GetMatTex(mat, pbrSG.specularGlossinessTexture, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULARGLOSSINESS_TEXTURE); + GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); + GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); m->pbrSpecularGlossiness = Nullable(pbrSG); } diff --git a/code/glTF2Exporter.h b/code/glTF2Exporter.h index 3aed35ae6..e9f7c113a 100644 --- a/code/glTF2Exporter.h +++ b/code/glTF2Exporter.h @@ -115,8 +115,8 @@ namespace Assimp void GetMatTex(const aiMaterial* mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot); void GetMatTex(const aiMaterial* mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot); void GetMatTex(const aiMaterial* mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot); - void GetMatColor(const aiMaterial* mat, glTF2::vec4& prop, const char* propName, int type, int idx); - void GetMatColor(const aiMaterial* mat, glTF2::vec3& prop, const char* propName, int type, int idx); + aiReturn GetMatColor(const aiMaterial* mat, glTF2::vec4& prop, const char* propName, int type, int idx); + aiReturn GetMatColor(const aiMaterial* mat, glTF2::vec3& prop, const char* propName, int type, int idx); void ExportMetadata(); void ExportMaterials(); void ExportMeshes(); diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index de9f39050..2faf1b926 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -228,7 +228,11 @@ void glTF2Importer::ImportMaterials(glTF2::Asset& r) } SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); + SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR); aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR); @@ -249,11 +253,11 @@ void glTF2Importer::ImportMaterials(glTF2::Asset& r) PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value; aimat->AddProperty(&mat.pbrSpecularGlossiness.isPresent, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS); - SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_FACTOR); SetMaterialColorProperty(r, pbrSG.specularFactor, aimat, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULAR_FACTOR); + SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.diffuseTexture, aimat, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_TEXTURE); SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.specularGlossinessTexture, aimat, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULARGLOSSINESS_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.diffuseTexture, aimat, aiTextureType_DIFFUSE); } } } From a898c1f2d1d579389d159ff1924766fcaf0a9dba Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Thu, 19 Oct 2017 12:24:25 -0400 Subject: [PATCH 249/490] SpecularFactor import and export improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The changes here (which only apply to reading from or writing to pbrSpecularGlossiness) will: - store and read specular color on `AI_MATKEY_COLOR_SPECULAR ` rather than `AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULAR_FACTOR` - store and read specular texture from `aiTextureType_SPECULAR` rather than `AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULARGLOSSINESS_TEXTURE`. Even though pbrSG’s specularGlossiness texture uses the alpha channel for glossiness, it will still work well enough with just the RGB channels of the image --- code/glTF2Asset.h | 2 -- code/glTF2Exporter.cpp | 4 ++-- code/glTF2Importer.cpp | 6 ++++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 84211aeb6..a98fe5ab2 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -172,9 +172,7 @@ namespace glTF2 #define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0 #define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0 #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 - #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULAR_FACTOR "$clr.specular", 0, 1 #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 - #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULARGLOSSINESS_TEXTURE aiTextureType_UNKNOWN, 1 #define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" #define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname" diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 26ff77913..1eb52a229 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -470,11 +470,11 @@ void glTF2Exporter::ExportMaterials() PbrSpecularGlossiness pbrSG; - GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULAR_FACTOR); mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR, pbrSG.glossinessFactor); - GetMatTex(mat, pbrSG.specularGlossinessTexture, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULARGLOSSINESS_TEXTURE); GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); + GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR); GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); + GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); m->pbrSpecularGlossiness = Nullable(pbrSG); } diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 2faf1b926..be7947b96 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -253,11 +253,13 @@ void glTF2Importer::ImportMaterials(glTF2::Asset& r) PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value; aimat->AddProperty(&mat.pbrSpecularGlossiness.isPresent, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS); - SetMaterialColorProperty(r, pbrSG.specularFactor, aimat, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULAR_FACTOR); SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); + SetMaterialColorProperty(r, pbrSG.specularFactor, aimat, AI_MATKEY_COLOR_SPECULAR); aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.specularGlossinessTexture, aimat, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULARGLOSSINESS_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.diffuseTexture, aimat, aiTextureType_DIFFUSE); + + SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.specularGlossinessTexture, aimat, aiTextureType_SPECULAR); } } } From 89358458f080ff74b515436980ef4432c4ea039f Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Thu, 19 Oct 2017 12:30:32 -0400 Subject: [PATCH 250/490] Approximate specularity / glossiness in metallicRoughness materials Before, models (of traditional lighting models) with specularity/glossiness would be completely flat when exported to metallicRoughness. These changes approximate glossiness (as an inverse of roughness, with specular intensity as a multiplier) both reading from gltf2 and writing to gltf2. --- code/glTF2Exporter.cpp | 34 ++++++++++++++++++++++++++++++++-- code/glTF2Importer.cpp | 7 +++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 1eb52a229..ad0e2647a 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -434,7 +434,29 @@ void glTF2Exporter::ExportMaterials() m->pbrMetallicRoughness.metallicFactor = 0; } - mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor); + // get roughness if source is gltf2 file + if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { + // otherwise, try to derive and convert from specular + shininess values + aiColor4D specularColor; + ai_real shininess; + + if ( + mat->Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS && + mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS + ) { + // convert specular color to luminance + float specularIntensity = specularColor[0] * 0.2125 + specularColor[1] * 0.7154 + specularColor[2] * 0.0721; + float roughnessFactor = 1 - std::sqrt(shininess / 1000); + + roughnessFactor = std::powf(roughnessFactor, 2); + roughnessFactor = std::min(std::max(roughnessFactor, 0.0f), 1.0f); + + // low specular intensity values should produce a rough material even if shininess is high. + roughnessFactor = 1 - (roughnessFactor * specularIntensity); + + m->pbrMetallicRoughness.roughnessFactor = roughnessFactor; + } + } GetMatTex(mat, m->normalTexture, aiTextureType_NORMALS); GetMatTex(mat, m->occlusionTexture, aiTextureType_LIGHTMAP); @@ -470,9 +492,17 @@ void glTF2Exporter::ExportMaterials() PbrSpecularGlossiness pbrSG; - mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR, pbrSG.glossinessFactor); GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR); + + if (mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) != AI_SUCCESS) { + float shininess; + + if (mat->Get(AI_MATKEY_SHININESS, shininess)) { + pbrSG.glossinessFactor = shininess / 1000; + } + } + GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index be7947b96..292e0eeb2 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -234,9 +234,13 @@ void glTF2Importer::ImportMaterials(glTF2::Asset& r) SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); + aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR); aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR); + float roughnessAsShininess = (1 - mat.pbrMetallicRoughness.roughnessFactor) * 1000; + aimat->AddProperty(&roughnessAsShininess, 1, AI_MATKEY_SHININESS); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.normalTexture, aimat, aiTextureType_NORMALS); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.occlusionTexture, aimat, aiTextureType_LIGHTMAP); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.emissiveTexture, aimat, aiTextureType_EMISSIVE); @@ -255,6 +259,9 @@ void glTF2Importer::ImportMaterials(glTF2::Asset& r) aimat->AddProperty(&mat.pbrSpecularGlossiness.isPresent, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS); SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); SetMaterialColorProperty(r, pbrSG.specularFactor, aimat, AI_MATKEY_COLOR_SPECULAR); + + float glossinessAsShininess = pbrSG.glossinessFactor * 1000.0f; + aimat->AddProperty(&glossinessAsShininess, 1, AI_MATKEY_SHININESS); aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR); SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.diffuseTexture, aimat, aiTextureType_DIFFUSE); From 40147d253d0c6f392085519159631db73b61bd57 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Thu, 19 Oct 2017 12:32:55 -0400 Subject: [PATCH 251/490] =?UTF-8?q?Prefer=20=E2=80=9CBLEND=E2=80=9D=20over?= =?UTF-8?q?=20=E2=80=9CMASK=E2=80=9D=20as=20an=20alphaMode=20default?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit “BLEND†is a much nicer alphaMode value (if the hardware supports it – not a steep requirement) than “MASK†as mask is either fully opaque or fully transparent, depending on the alphaCutoff. This matches many other converters’ alphaMode default. --- code/glTF2Exporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index ad0e2647a..46f201b52 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -475,7 +475,7 @@ void glTF2Exporter::ExportMaterials() if (mat->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { if (opacity < 1) { - m->alphaMode = "MASK"; + m->alphaMode = "BLEND"; m->pbrMetallicRoughness.baseColorFactor[3] *= opacity; } } From 6e888386029a4265a27e5480271c6ca381b26def Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Thu, 19 Oct 2017 13:40:03 -0400 Subject: [PATCH 252/490] powf -> pow Fix build errors on linux --- code/glTF2Exporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 46f201b52..9e6415522 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -448,7 +448,7 @@ void glTF2Exporter::ExportMaterials() float specularIntensity = specularColor[0] * 0.2125 + specularColor[1] * 0.7154 + specularColor[2] * 0.0721; float roughnessFactor = 1 - std::sqrt(shininess / 1000); - roughnessFactor = std::powf(roughnessFactor, 2); + roughnessFactor = std::pow(roughnessFactor, 2); roughnessFactor = std::min(std::max(roughnessFactor, 0.0f), 1.0f); // low specular intensity values should produce a rough material even if shininess is high. From cd4ef0a2e90c06a30e7359c133f44a8cf232c65e Mon Sep 17 00:00:00 2001 From: Victor NG Date: Sat, 21 Oct 2017 18:15:46 +0800 Subject: [PATCH 253/490] Update ColladaExporter.h --- code/ColladaExporter.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/code/ColladaExporter.h b/code/ColladaExporter.h index c78272726..e7a4a9b5d 100644 --- a/code/ColladaExporter.h +++ b/code/ColladaExporter.h @@ -114,7 +114,9 @@ protected: /// Writes the given mesh void WriteGeometry( size_t pIndex); - enum FloatDataType { FloatType_Vector, FloatType_TexCoord2, FloatType_TexCoord3, FloatType_Color, FloatType_Mat4x4, FloatType_Weight }; + //enum FloatDataType { FloatType_Vector, FloatType_TexCoord2, FloatType_TexCoord3, FloatType_Color, FloatType_Mat4x4, FloatType_Weight }; + // customized to add animation related type + enum FloatDataType { FloatType_Vector, FloatType_TexCoord2, FloatType_TexCoord3, FloatType_Color, FloatType_Mat4x4, FloatType_Weight, FloatType_Time }; /// Writes a float array of the given type void WriteFloatArray( const std::string& pIdString, FloatDataType pType, const ai_real* pData, size_t pElementCount); @@ -122,6 +124,11 @@ protected: /// Writes the scene library void WriteSceneLibrary(); + // customized, Writes the animation library + void WriteAnimationsLibrary(); + void WriteAnimationLibrary( size_t pIndex); + std::string mFoundSkeletonRootNodeID = "skeleton_root"; // will be replaced by found node id in the WriteNode call. + /// Recursively writes the given node void WriteNode( const aiScene* scene, aiNode* pNode); From 90ba199ad412aa07400fb55af25d87fd93a2d421 Mon Sep 17 00:00:00 2001 From: Victor NG Date: Sat, 21 Oct 2017 18:28:14 +0800 Subject: [PATCH 254/490] Update ColladaExporter.cpp --- code/ColladaExporter.cpp | 254 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 250 insertions(+), 4 deletions(-) diff --git a/code/ColladaExporter.cpp b/code/ColladaExporter.cpp index 06604bc8e..52562a9f7 100644 --- a/code/ColladaExporter.cpp +++ b/code/ColladaExporter.cpp @@ -137,6 +137,9 @@ void ColladaExporter::WriteFile() WriteControllerLibrary(); WriteSceneLibrary(); + + // customized, Writes the animation library + WriteAnimationsLibrary(); // useless Collada fu at the end, just in case we haven't had enough indirections, yet. mOutput << startstr << "" << endstr; @@ -1125,6 +1128,7 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy case FloatType_Color: floatsPerElement = 3; break; case FloatType_Mat4x4: floatsPerElement = 16; break; case FloatType_Weight: floatsPerElement = 1; break; + case FloatType_Time: floatsPerElement = 1; break; default: return; } @@ -1201,7 +1205,13 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy case FloatType_Weight: mOutput << startstr << "" << endstr; break; - } + + // customized, add animation related + case FloatType_Time: + mOutput << startstr << "" << endstr; + break; + + } PopTag(); mOutput << startstr << "" << endstr; @@ -1231,7 +1241,170 @@ void ColladaExporter::WriteSceneLibrary() PopTag(); mOutput << startstr << "" << endstr; } +// ------------------------------------------------------------------------------------------------ +void ColladaExporter::WriteAnimationLibrary(size_t pIndex) +{ + const aiAnimation * anim = mScene->mAnimations[pIndex]; + + if ( anim->mNumChannels == 0 && anim->mNumMeshChannels == 0 && anim->mNumMorphMeshChannels ==0 ) + return; + + const std::string animation_name_escaped = XMLEscape( anim->mName.C_Str() ); + std::string idstr = anim->mName.C_Str(); + std::string ending = std::string( "AnimId" ) + to_string(pIndex); + if (idstr.length() >= ending.length()) { + if (0 != idstr.compare (idstr.length() - ending.length(), ending.length(), ending)) { + idstr = idstr + ending; + } + } else { + idstr = idstr + ending; + } + const std::string idstrEscaped = XMLEscape(idstr); + + mOutput << startstr << "" << endstr; + PushTag(); + + for (size_t a = 0; a < anim->mNumChannels; ++a) { + const aiNodeAnim * nodeAnim = anim->mChannels[a]; + + // sanity check + if ( nodeAnim->mNumPositionKeys != nodeAnim->mNumScalingKeys || nodeAnim->mNumPositionKeys != nodeAnim->mNumRotationKeys ) continue; + + { + const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-input"); + + std::vector frames; + for( size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) { + frames.push_back(nodeAnim->mPositionKeys[i].mTime); + } + + WriteFloatArray( node_idstr , FloatType_Time, (const ai_real*) frames.data(), frames.size()); + frames.clear(); + } + + { + const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-output"); + + std::vector keyframes; + keyframes.reserve(nodeAnim->mNumPositionKeys * 16); + for( size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) { + + aiVector3D Scaling = nodeAnim->mScalingKeys[i].mValue; + aiMatrix4x4 ScalingM; // identity + ScalingM[0][0] = Scaling.x; ScalingM[1][1] = Scaling.y; ScalingM[2][2] = Scaling.z; + + aiQuaternion RotationQ = nodeAnim->mRotationKeys[i].mValue; + aiMatrix4x4 s = aiMatrix4x4( RotationQ.GetMatrix() ); + aiMatrix4x4 RotationM(s.a1, s.a2, s.a3, 0, s.b1, s.b2, s.b3, 0, s.c1, s.c2, s.c3, 0, 0, 0, 0, 1); + + aiVector3D Translation = nodeAnim->mPositionKeys[i].mValue; + aiMatrix4x4 TranslationM; // identity + TranslationM[0][3] = Translation.x; TranslationM[1][3] = Translation.y; TranslationM[2][3] = Translation.z; + + // Combine the above transformations + aiMatrix4x4 mat = TranslationM * RotationM * ScalingM; + + for( uint j = 0; j < 4; ++j) + keyframes.insert(keyframes.end(), mat[j], mat[j] + 4); + } + + WriteFloatArray( node_idstr, FloatType_Mat4x4, (const ai_real*) keyframes.data(), keyframes.size() / 16); + } + + { + std::vector names; + for (int i = 0; i < nodeAnim->mNumPositionKeys; ++i) { + if ( nodeAnim->mPreState == aiAnimBehaviour_DEFAULT + || nodeAnim->mPreState == aiAnimBehaviour_LINEAR + || nodeAnim->mPreState == aiAnimBehaviour_REPEAT + ) { + names.push_back( "LINEAR" ); + } else if (nodeAnim->mPostState == aiAnimBehaviour_CONSTANT) { + names.push_back( "STEP" ); + } + } + + const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-interpolation"); + std::string arrayId = node_idstr + "-array"; + + mOutput << startstr << "" << endstr; + PushTag(); + + // source array + mOutput << startstr << " "; + for( size_t a = 0; a < names.size(); ++a ) + mOutput << names[a] << " "; + mOutput << "" << endstr; + + mOutput << startstr << "" << endstr; + PushTag(); + + mOutput << startstr << "" << endstr; + PushTag(); + + mOutput << startstr << "" << endstr; + + PopTag(); + mOutput << startstr << "" << endstr; + + PopTag(); + mOutput << startstr << "" << endstr; + + PopTag(); + mOutput << startstr << "" << endstr; + } + + } + + for (size_t a = 0; a < anim->mNumChannels; ++a) { + const aiNodeAnim * nodeAnim = anim->mChannels[a]; + + { + // samplers + const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-sampler"); + mOutput << startstr << "" << endstr; + PushTag(); + + mOutput << startstr << "mNodeName.data + std::string("_matrix-input") ) << "\"/>" << endstr; + mOutput << startstr << "mNodeName.data + std::string("_matrix-output") ) << "\"/>" << endstr; + mOutput << startstr << "mNodeName.data + std::string("_matrix-interpolation") ) << "\"/>" << endstr; + + PopTag(); + mOutput << startstr << "" << endstr; + } + } + + for (size_t a = 0; a < anim->mNumChannels; ++a) { + const aiNodeAnim * nodeAnim = anim->mChannels[a]; + + { + // channels + mOutput << startstr << "mNodeName.data + std::string("_matrix-sampler") ) << "\" target=\"" << XMLEscape(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr; + } + } + + PopTag(); + mOutput << startstr << "" << endstr; + +} +// ------------------------------------------------------------------------------------------------ +void ColladaExporter::WriteAnimationsLibrary() +{ + const std::string scene_name_escaped = XMLEscape(mScene->mRootNode->mName.C_Str()); + + if ( mScene->mNumAnimations > 0 ) { + mOutput << startstr << "" << endstr; + PushTag(); + + // start recursive write at the root node + for( size_t a = 0; a < mScene->mNumAnimations; ++a) + WriteAnimationLibrary( a ); + + PopTag(); + mOutput << startstr << "" << endstr; + } +} // ------------------------------------------------------------------------------------------------ // Helper to find a bone by name in the scene aiBone* findBone( const aiScene* scene, const char * name) { @@ -1247,6 +1420,59 @@ aiBone* findBone( const aiScene* scene, const char * name) { return NULL; } +// ------------------------------------------------------------------------------------------------ +const aiNode * findBoneNode( const aiNode* aNode, const aiBone* bone) +{ + if ( aNode && bone && aNode->mName == bone->mName ) { + return aNode; + } + + if ( aNode && bone ) { + for (unsigned int i=0; i < aNode->mNumChildren; ++i) { + aiNode * aChild = aNode->mChildren[i]; + const aiNode * foundFromChild = 0; + if ( aChild ) { + foundFromChild = findBoneNode( aChild, bone ); + if ( foundFromChild ) return foundFromChild; + } + } + } + + return NULL; +} + +const aiNode * findSkeletonRootNode( const aiScene* scene, const aiMesh * mesh) +{ + std::set topParentBoneNodes; + if ( mesh && mesh->mNumBones > 0 ) { + for (unsigned int i=0; i < mesh->mNumBones; ++i) { + aiBone * bone = mesh->mBones[i]; + + const aiNode * node = findBoneNode( scene->mRootNode, bone); + if ( node ) { + while ( node->mParent && findBone(scene, node->mParent->mName.C_Str() ) != 0 ) { + node = node->mParent; + } + topParentBoneNodes.insert( node ); + } + } + } + + if ( !topParentBoneNodes.empty() ) { + const aiNode * parentBoneNode = *topParentBoneNodes.begin(); + if ( topParentBoneNodes.size() == 1 ) { + return parentBoneNode; + } else { + for (auto it : topParentBoneNodes) { + if ( it->mParent ) return it->mParent; + } + return parentBoneNode; + } + } + + return NULL; +} + // ------------------------------------------------------------------------------------------------ // Recursively writes the given node void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode) @@ -1274,12 +1500,22 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode) } const std::string node_name_escaped = XMLEscape(pNode->mName.data); + /* // customized, Note! the id field is crucial for inter-xml look up, it cannot be replaced with sid ?! mOutput << startstr << "" << endstr; PushTag(); @@ -1287,7 +1523,11 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode) // write transformation - we can directly put the matrix there // TODO: (thom) decompose into scale - rot - quad to allow addressing it by animations afterwards const aiMatrix4x4& mat = pNode->mTransformation; - mOutput << startstr << ""; + + // customized, sid should be 'matrix' to match with loader code. + //mOutput << startstr << ""; + mOutput << startstr << ""; + mOutput << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << " "; mOutput << mat.b1 << " " << mat.b2 << " " << mat.b3 << " " << mat.b4 << " "; mOutput << mat.c1 << " " << mat.c2 << " " << mat.c3 << " " << mat.c4 << " "; @@ -1331,7 +1571,13 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode) << endstr; PushTag(); - mOutput << startstr << "#skeleton_root" << endstr; + // note! this mFoundSkeletonRootNodeID some how affects animation, it makes the mesh attaches to armature skeleton root node. + // use the first bone to find skeleton root + const aiNode * skeletonRootBoneNode = findSkeletonRootNode( pScene, mesh ); + if ( skeletonRootBoneNode ) { + mFoundSkeletonRootNodeID = XMLEscape( skeletonRootBoneNode->mName.C_Str() ); + } + mOutput << startstr << "#" << mFoundSkeletonRootNodeID << "" << endstr; } mOutput << startstr << "" << endstr; PushTag(); From cc8374dd80821a423c41aa1ff85c98d12ca8e015 Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Sat, 21 Oct 2017 20:36:43 +0200 Subject: [PATCH 255/490] Return exception when obj file contains invalid face indice --- code/ObjFileParser.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/ObjFileParser.cpp b/code/ObjFileParser.cpp index acf275b94..4b203a8c2 100644 --- a/code/ObjFileParser.cpp +++ b/code/ObjFileParser.cpp @@ -475,7 +475,11 @@ void ObjFileParser::getFace( aiPrimitiveType type ) { } else { reportErrorTokenInFace(); } + } else { + //On error, std::atoi will return 0 which is not a valid value + throw DeadlyImportError("OBJ: Invalid face indice"); } + } m_DataIt += iStep; } From 8b73ec7541e89e870140efa49e829fee5a32b9a0 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Thu, 26 Oct 2017 11:33:33 -0400 Subject: [PATCH 256/490] Fix shininess to roughness conversion; Add comments Was accidentally flipping to value (1 - x) twice, thus negating the effect. --- code/glTF2Exporter.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 9e6415522..d8cff897c 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -446,15 +446,15 @@ void glTF2Exporter::ExportMaterials() ) { // convert specular color to luminance float specularIntensity = specularColor[0] * 0.2125 + specularColor[1] * 0.7154 + specularColor[2] * 0.0721; - float roughnessFactor = 1 - std::sqrt(shininess / 1000); - - roughnessFactor = std::pow(roughnessFactor, 2); - roughnessFactor = std::min(std::max(roughnessFactor, 0.0f), 1.0f); + //normalize shininess (assuming max is 1000) with an inverse exponentional curve + float normalizedShininess = std::sqrt(shininess / 1000); + //clamp the shininess value between 0 and 1 + normalizedShininess = std::min(std::max(normalizedShininess, 0.0f), 1.0f); // low specular intensity values should produce a rough material even if shininess is high. - roughnessFactor = 1 - (roughnessFactor * specularIntensity); + normalizedShininess = normalizedShininess * specularIntensity; - m->pbrMetallicRoughness.roughnessFactor = roughnessFactor; + m->pbrMetallicRoughness.roughnessFactor = 1 - normalizedShininess; } } From 18cef9b391464d9facc038f0731ace627e432b7c Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 26 Oct 2017 17:59:09 +0200 Subject: [PATCH 257/490] [ObjExporter] add a "no mtl" version of the obj exporter --- code/Exporter.cpp | 3 +++ code/ObjExporter.cpp | 37 +++++++++++++++++++++++++++++++------ code/ObjExporter.h | 4 ++-- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 1e2d7cbf1..eba3a54b7 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -83,6 +83,7 @@ void ExportSceneCollada(const char*,IOSystem*, const aiScene*, const ExportPrope void ExportSceneXFile(const char*,IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneStep(const char*,IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneObj(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneObjNoMtl(const char*,IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneSTL(const char*,IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneSTLBinary(const char*,IOSystem*, const aiScene*, const ExportProperties*); void ExportScenePly(const char*,IOSystem*, const aiScene*, const ExportProperties*); @@ -115,6 +116,8 @@ Exporter::ExportFormatEntry gExporters[] = #ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER Exporter::ExportFormatEntry( "obj", "Wavefront OBJ format", "obj", &ExportSceneObj, aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */), + Exporter::ExportFormatEntry( "objnomtl", "Wavefront OBJ format without material file", "obj", &ExportSceneObjNoMtl, + aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */), #endif #ifndef ASSIMP_BUILD_NO_STL_EXPORTER diff --git a/code/ObjExporter.cpp b/code/ObjExporter.cpp index 692eac0e6..ba18a7fa3 100644 --- a/code/ObjExporter.cpp +++ b/code/ObjExporter.cpp @@ -83,12 +83,34 @@ void ExportSceneObj(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene } } +// ------------------------------------------------------------------------------------------------ +// Worker function for exporting a scene to Wavefront OBJ without the material file. Prototyped and registered in Exporter.cpp +void ExportSceneObjNoMtl(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties) { + // invoke the exporter + ObjExporter exporter(pFile, pScene, true); + + if (exporter.mOutput.fail() || exporter.mOutputMat.fail()) { + throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); + } + + // we're still here - export successfully completed. Write both the main OBJ file and the material script + { + std::unique_ptr outfile (pIOSystem->Open(pFile,"wt")); + if(outfile == NULL) { + throw DeadlyExportError("could not open output .obj file: " + std::string(pFile)); + } + outfile->Write( exporter.mOutput.str().c_str(), static_cast(exporter.mOutput.tellp()),1); + } + + +} + } // end of namespace Assimp static const std::string MaterialExt = ".mtl"; // ------------------------------------------------------------------------------------------------ -ObjExporter::ObjExporter(const char* _filename, const aiScene* pScene) +ObjExporter::ObjExporter(const char* _filename, const aiScene* pScene, bool noMtl) : filename(_filename) , pScene(pScene) , vp() @@ -108,8 +130,9 @@ ObjExporter::ObjExporter(const char* _filename, const aiScene* pScene) mOutputMat.imbue(l); mOutputMat.precision(16); - WriteGeometryFile(); - WriteMaterialFile(); + WriteGeometryFile(noMtl); + if (!noMtl) + WriteMaterialFile(); } // ------------------------------------------------------------------------------------------------ @@ -236,9 +259,10 @@ void ObjExporter::WriteMaterialFile() } // ------------------------------------------------------------------------------------------------ -void ObjExporter::WriteGeometryFile() { +void ObjExporter::WriteGeometryFile(bool noMtl) { WriteHeader(mOutput); - mOutput << "mtllib " << GetMaterialLibName() << endl << endl; + if (!noMtl) + mOutput << "mtllib " << GetMaterialLibName() << endl << endl; // collect mesh geometry aiMatrix4x4 mBase; @@ -284,7 +308,8 @@ void ObjExporter::WriteGeometryFile() { if (!m.name.empty()) { mOutput << "g " << m.name << endl; } - mOutput << "usemtl " << m.matname << endl; + if (!noMtl) + mOutput << "usemtl " << m.matname << endl; for(const Face& f : m.faces) { mOutput << f.kind << ' '; diff --git a/code/ObjExporter.h b/code/ObjExporter.h index fb60402d7..391f8416d 100644 --- a/code/ObjExporter.h +++ b/code/ObjExporter.h @@ -62,7 +62,7 @@ namespace Assimp { class ObjExporter { public: /// Constructor for a specific scene to export - ObjExporter(const char* filename, const aiScene* pScene); + ObjExporter(const char* filename, const aiScene* pScene, bool noMtl=false); ~ObjExporter(); std::string GetMaterialLibName(); std::string GetMaterialLibFileName(); @@ -97,7 +97,7 @@ private: void WriteHeader(std::ostringstream& out); void WriteMaterialFile(); - void WriteGeometryFile(); + void WriteGeometryFile(bool noMtl=false); std::string GetMaterialName(unsigned int index); void AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat); void AddNode(const aiNode* nd, const aiMatrix4x4& mParent); From a6688243a77118d3a81b0b40cf7e42a521c38e2c Mon Sep 17 00:00:00 2001 From: Thomas Lemaire Date: Fri, 27 Oct 2017 10:14:53 +0200 Subject: [PATCH 258/490] [ObjExporter] add a test for the "no mtl" version of the obj exporter --- test/unit/utObjImportExport.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/utObjImportExport.cpp b/test/unit/utObjImportExport.cpp index 3b08df80f..5783c049e 100644 --- a/test/unit/utObjImportExport.cpp +++ b/test/unit/utObjImportExport.cpp @@ -205,6 +205,7 @@ protected: const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", 0 ); EXPECT_NE( nullptr, scene ); EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "obj", ASSIMP_TEST_MODELS_DIR "/OBJ/spider_test.obj" ) ); + EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "objnomtl", ASSIMP_TEST_MODELS_DIR "/OBJ/spider_nomtl_test.obj" ) ); return true; } From c1c4a5ed2a689dbd6b870682e9db51013a7cc7f8 Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Sun, 29 Oct 2017 15:12:56 +0100 Subject: [PATCH 259/490] Add two unit tests for OBJ importer --- test/unit/utObjImportExport.cpp | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/test/unit/utObjImportExport.cpp b/test/unit/utObjImportExport.cpp index 3b08df80f..800b68b22 100644 --- a/test/unit/utObjImportExport.cpp +++ b/test/unit/utObjImportExport.cpp @@ -305,3 +305,38 @@ TEST_F(utObjImportExport, relative_indices_Test) { } } + +TEST_F(utObjImportExport, homogeneous_coordinates_Test) { + static const std::string ObjModel = + "v -0.500000 0.000000 0.400000 0.50000\n" + "v -0.500000 0.000000 -0.800000 1.00000\n" + "v 0.500000 1.000000 -0.800000 0.5000\n" + "f 1 2 3\nB"; + + Assimp::Importer myimporter; + const aiScene *scene = myimporter.ReadFileFromMemory(ObjModel.c_str(), ObjModel.size(), aiProcess_ValidateDataStructure); + EXPECT_NE(nullptr, scene); + + EXPECT_EQ(scene->mNumMeshes, 1); + const aiMesh *mesh = scene->mMeshes[0]; + EXPECT_EQ(mesh->mNumVertices, 3); + EXPECT_EQ(mesh->mNumFaces, 1); + const aiFace face = mesh->mFaces[0]; + EXPECT_EQ(face.mNumIndices, 3); + const aiVector3D vertice = mesh->mVertices[0]; + EXPECT_EQ(vertice.x, -1.0f); + EXPECT_EQ(vertice.y, 0.0f); + EXPECT_EQ(vertice.z, 0.8f); +} + +TEST_F(utObjImportExport, 0based_array_Test) { + static const std::string ObjModel = + "v -0.500000 0.000000 0.400000\n" + "v -0.500000 0.000000 -0.800000\n" + "v -0.500000 1.000000 -0.800000\n" + "f 0 1 2\nB"; + + Assimp::Importer myimporter; + const aiScene *scene = myimporter.ReadFileFromMemory(ObjModel.c_str(), ObjModel.size(), 0); + EXPECT_EQ(nullptr, scene); +} From f49de6ecfe1ee4bf542e97d6a5443ddad38ce50d Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 29 Oct 2017 20:28:59 +0100 Subject: [PATCH 260/490] closes https://github.com/assimp/assimp/issues/1514: add postprocess step for scaling --- code/CMakeLists.txt | 2 + code/DeboneProcess.h | 2 +- code/FixNormalsStep.h | 5 +- code/IFCCurve.cpp | 150 +++++++++++++------------------------ code/LineSplitter.h | 20 ++--- code/STEPFile.h | 2 +- code/ScaleProcess.cpp | 93 +++++++++++++++++++++++ code/ScaleProcess.h | 70 +++++++++++++++++ include/assimp/config.h.in | 8 ++ 9 files changed, 234 insertions(+), 118 deletions(-) create mode 100644 code/ScaleProcess.cpp create mode 100644 code/ScaleProcess.h diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index e87dad550..4890a3c9e 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -156,6 +156,8 @@ SET( Common_SRCS SkeletonMeshBuilder.h SplitByBoneCountProcess.cpp SplitByBoneCountProcess.h + ScaleProcess.cpp + ScaleProcess.h SmoothingGroups.h StandardShapes.cpp StandardShapes.h diff --git a/code/DeboneProcess.h b/code/DeboneProcess.h index 75f158c03..8d4607bb9 100644 --- a/code/DeboneProcess.h +++ b/code/DeboneProcess.h @@ -1,4 +1,4 @@ - /* +/* Open Asset Import Library (assimp) ---------------------------------------------------------------------- diff --git a/code/FixNormalsStep.h b/code/FixNormalsStep.h index b47155ccd..71309c7e5 100644 --- a/code/FixNormalsStep.h +++ b/code/FixNormalsStep.h @@ -56,14 +56,11 @@ namespace Assimp * vectors of an object are facing inwards. In this case they will be * flipped. */ -class FixInfacingNormalsProcess : public BaseProcess -{ +class FixInfacingNormalsProcess : public BaseProcess { public: - FixInfacingNormalsProcess(); ~FixInfacingNormalsProcess(); -public: // ------------------------------------------------------------------- /** Returns whether the processing step is present in the given flag field. * @param pFlags The processing flags the importer was called with. A bitwise diff --git a/code/IFCCurve.cpp b/code/IFCCurve.cpp index 176fe3c13..4a8026551 100644 --- a/code/IFCCurve.cpp +++ b/code/IFCCurve.cpp @@ -43,28 +43,22 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * @brief Read profile and curves entities from IFC files */ - - #ifndef ASSIMP_BUILD_NO_IFC_IMPORTER #include "IFCUtil.h" namespace Assimp { - namespace IFC { - namespace { +namespace IFC { +namespace { // -------------------------------------------------------------------------------- // Conic is the base class for Circle and Ellipse // -------------------------------------------------------------------------------- -class Conic : public Curve -{ - +class Conic : public Curve { public: - // -------------------------------------------------- Conic(const IfcConic& entity, ConversionData& conv) - : Curve(entity,conv) - { + : Curve(entity,conv) { IfcMatrix4 trafo; ConvertAxisPlacement(trafo,*entity.Position,conv); @@ -75,8 +69,6 @@ public: p[2] = IfcVector3(trafo.a3,trafo.b3,trafo.c3); } -public: - // -------------------------------------------------- bool IsClosed() const { return true; @@ -84,7 +76,8 @@ public: // -------------------------------------------------- size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const { - ai_assert(InRange(a) && InRange(b)); + ai_assert( InRange( a ) ); + ai_assert( InRange( b ) ); a *= conv.angle_scale; b *= conv.angle_scale; @@ -104,15 +97,11 @@ protected: IfcVector3 location, p[3]; }; - // -------------------------------------------------------------------------------- // Circle // -------------------------------------------------------------------------------- -class Circle : public Conic -{ - +class Circle : public Conic { public: - // -------------------------------------------------- Circle(const IfcCircle& entity, ConversionData& conv) : Conic(entity,conv) @@ -120,8 +109,6 @@ public: { } -public: - // -------------------------------------------------- IfcVector3 Eval(IfcFloat u) const { u = -conv.angle_scale * u; @@ -137,20 +124,15 @@ private: // -------------------------------------------------------------------------------- // Ellipse // -------------------------------------------------------------------------------- -class Ellipse : public Conic -{ - +class Ellipse : public Conic { public: - // -------------------------------------------------- Ellipse(const IfcEllipse& entity, ConversionData& conv) - : Conic(entity,conv) - , entity(entity) - { + : Conic(entity,conv) + , entity(entity) { + // empty } -public: - // -------------------------------------------------- IfcVector3 Eval(IfcFloat u) const { u = -conv.angle_scale * u; @@ -162,25 +144,18 @@ private: const IfcEllipse& entity; }; - // -------------------------------------------------------------------------------- // Line // -------------------------------------------------------------------------------- -class Line : public Curve -{ - +class Line : public Curve { public: - // -------------------------------------------------- Line(const IfcLine& entity, ConversionData& conv) - : Curve(entity,conv) - { + : Curve(entity,conv) { ConvertCartesianPoint(p,entity.Pnt); ConvertVector(v,entity.Dir); } -public: - // -------------------------------------------------- bool IsClosed() const { return false; @@ -193,16 +168,17 @@ public: // -------------------------------------------------- size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const { - ai_assert(InRange(a) && InRange(b)); + ai_assert( InRange( a ) ); + ai_assert( InRange( b ) ); // two points are always sufficient for a line segment return a==b ? 1 : 2; } // -------------------------------------------------- - void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const - { - ai_assert(InRange(a) && InRange(b)); + void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const { + ai_assert( InRange( a ) ); + ai_assert( InRange( b ) ); if (a == b) { out.verts.push_back(Eval(a)); @@ -227,18 +203,14 @@ private: // -------------------------------------------------------------------------------- // CompositeCurve joins multiple smaller, bounded curves // -------------------------------------------------------------------------------- -class CompositeCurve : public BoundedCurve -{ - +class CompositeCurve : public BoundedCurve { typedef std::pair< std::shared_ptr< BoundedCurve >, bool > CurveEntry; public: - // -------------------------------------------------- CompositeCurve(const IfcCompositeCurve& entity, ConversionData& conv) - : BoundedCurve(entity,conv) - , total() - { + : BoundedCurve(entity,conv) + , total() { curves.reserve(entity.Segments.size()); for(const IfcCompositeCurveSegment& curveSegment :entity.Segments) { // according to the specification, this must be a bounded curve @@ -263,8 +235,6 @@ public: } } -public: - // -------------------------------------------------- IfcVector3 Eval(IfcFloat u) const { if (curves.empty()) { @@ -287,7 +257,8 @@ public: // -------------------------------------------------- size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const { - ai_assert(InRange(a) && InRange(b)); + ai_assert( InRange( a ) ); + ai_assert( InRange( b ) ); size_t cnt = 0; IfcFloat acc = 0; @@ -306,9 +277,9 @@ public: } // -------------------------------------------------- - void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const - { - ai_assert(InRange(a) && InRange(b)); + void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const { + ai_assert( InRange( a ) ); + ai_assert( InRange( b ) ); const size_t cnt = EstimateSampleCount(a,b); out.verts.reserve(out.verts.size() + cnt); @@ -330,19 +301,14 @@ public: private: std::vector< CurveEntry > curves; - IfcFloat total; }; - // -------------------------------------------------------------------------------- // TrimmedCurve can be used to trim an unbounded curve to a bounded range // -------------------------------------------------------------------------------- -class TrimmedCurve : public BoundedCurve -{ - +class TrimmedCurve : public BoundedCurve { public: - // -------------------------------------------------- TrimmedCurve(const IfcTrimmedCurve& entity, ConversionData& conv) : BoundedCurve(entity,conv) @@ -409,8 +375,6 @@ public: ai_assert(maxval >= 0); } -public: - // -------------------------------------------------- IfcVector3 Eval(IfcFloat p) const { ai_assert(InRange(p)); @@ -419,7 +383,8 @@ public: // -------------------------------------------------- size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const { - ai_assert(InRange(a) && InRange(b)); + ai_assert( InRange( a ) ); + ai_assert( InRange( b ) ); return base->EstimateSampleCount(TrimParam(a),TrimParam(b)); } @@ -435,13 +400,11 @@ public: } private: - // -------------------------------------------------- IfcFloat TrimParam(IfcFloat f) const { return agree_sense ? f + range.first : range.second - f; } - private: ParamRange range; IfcFloat maxval; @@ -454,11 +417,8 @@ private: // -------------------------------------------------------------------------------- // PolyLine is a 'curve' defined by linear interpolation over a set of discrete points // -------------------------------------------------------------------------------- -class PolyLine : public BoundedCurve -{ - +class PolyLine : public BoundedCurve { public: - // -------------------------------------------------- PolyLine(const IfcPolyline& entity, ConversionData& conv) : BoundedCurve(entity,conv) @@ -472,8 +432,6 @@ public: } } -public: - // -------------------------------------------------- IfcVector3 Eval(IfcFloat p) const { ai_assert(InRange(p)); @@ -502,13 +460,10 @@ private: std::vector points; }; - } // anon - // ------------------------------------------------------------------------------------------------ -Curve* Curve :: Convert(const IFC::IfcCurve& curve,ConversionData& conv) -{ +Curve* Curve::Convert(const IFC::IfcCurve& curve,ConversionData& conv) { if(curve.ToPtr()) { if(const IfcPolyline* c = curve.ToPtr()) { return new PolyLine(*c,conv); @@ -519,9 +474,6 @@ Curve* Curve :: Convert(const IFC::IfcCurve& curve,ConversionData& conv) if(const IfcCompositeCurve* c = curve.ToPtr()) { return new CompositeCurve(*c,conv); } - //if(const IfcBSplineCurve* c = curve.ToPtr()) { - // return new BSplineCurve(*c,conv); - //} } if(curve.ToPtr()) { @@ -543,8 +495,7 @@ Curve* Curve :: Convert(const IFC::IfcCurve& curve,ConversionData& conv) #ifdef ASSIMP_BUILD_DEBUG // ------------------------------------------------------------------------------------------------ -bool Curve :: InRange(IfcFloat u) const -{ +bool Curve::InRange(IfcFloat u) const { const ParamRange range = GetParametricRange(); if (IsClosed()) { return true; @@ -555,24 +506,23 @@ bool Curve :: InRange(IfcFloat u) const #endif // ------------------------------------------------------------------------------------------------ -IfcFloat Curve :: GetParametricRangeDelta() const -{ +IfcFloat Curve::GetParametricRangeDelta() const { const ParamRange& range = GetParametricRange(); return std::abs(range.second - range.first); } // ------------------------------------------------------------------------------------------------ -size_t Curve :: EstimateSampleCount(IfcFloat a, IfcFloat b) const -{ - ai_assert(InRange(a) && InRange(b)); +size_t Curve::EstimateSampleCount(IfcFloat a, IfcFloat b) const { + ai_assert( InRange( a ) ); + ai_assert( InRange( b ) ); // arbitrary default value, deriving classes should supply better suited values return 16; } // ------------------------------------------------------------------------------------------------ -IfcFloat RecursiveSearch(const Curve* cv, const IfcVector3& val, IfcFloat a, IfcFloat b, unsigned int samples, IfcFloat threshold, unsigned int recurse = 0, unsigned int max_recurse = 15) -{ +IfcFloat RecursiveSearch(const Curve* cv, const IfcVector3& val, IfcFloat a, IfcFloat b, + unsigned int samples, IfcFloat threshold, unsigned int recurse = 0, unsigned int max_recurse = 15) { ai_assert(samples>1); const IfcFloat delta = (b-a)/samples, inf = std::numeric_limits::infinity(); @@ -594,7 +544,8 @@ IfcFloat RecursiveSearch(const Curve* cv, const IfcVector3& val, IfcFloat a, Ifc } } - ai_assert(min_diff[0] != inf && min_diff[1] != inf); + ai_assert( min_diff[ 0 ] != inf ); + ai_assert( min_diff[ 1 ] != inf ); if ( std::fabs(a-min_point[0]) < threshold || recurse >= max_recurse) { return min_point[0]; } @@ -615,15 +566,15 @@ IfcFloat RecursiveSearch(const Curve* cv, const IfcVector3& val, IfcFloat a, Ifc } // ------------------------------------------------------------------------------------------------ -bool Curve :: ReverseEval(const IfcVector3& val, IfcFloat& paramOut) const +bool Curve::ReverseEval(const IfcVector3& val, IfcFloat& paramOut) const { // note: the following algorithm is not guaranteed to find the 'right' parameter value // in all possible cases, but it will always return at least some value so this function // will never fail in the default implementation. // XXX derive threshold from curve topology - const IfcFloat threshold = 1e-4f; - const unsigned int samples = 16; + static const IfcFloat threshold = 1e-4f; + static const unsigned int samples = 16; const ParamRange& range = GetParametricRange(); paramOut = RecursiveSearch(this,val,range.first,range.second,samples,threshold); @@ -632,9 +583,9 @@ bool Curve :: ReverseEval(const IfcVector3& val, IfcFloat& paramOut) const } // ------------------------------------------------------------------------------------------------ -void Curve :: SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const -{ - ai_assert(InRange(a) && InRange(b)); +void Curve::SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const { + ai_assert( InRange( a ) ); + ai_assert( InRange( b ) ); const size_t cnt = std::max(static_cast(0),EstimateSampleCount(a,b)); out.verts.reserve( out.verts.size() + cnt + 1); @@ -646,16 +597,15 @@ void Curve :: SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const } // ------------------------------------------------------------------------------------------------ -bool BoundedCurve :: IsClosed() const -{ +bool BoundedCurve::IsClosed() const { return false; } // ------------------------------------------------------------------------------------------------ -void BoundedCurve :: SampleDiscrete(TempMesh& out) const -{ +void BoundedCurve::SampleDiscrete(TempMesh& out) const { const ParamRange& range = GetParametricRange(); - ai_assert(range.first != std::numeric_limits::infinity() && range.second != std::numeric_limits::infinity()); + ai_assert( range.first != std::numeric_limits::infinity() ); + ai_assert( range.second != std::numeric_limits::infinity() ); return SampleDiscrete(out,range.first,range.second); } diff --git a/code/LineSplitter.h b/code/LineSplitter.h index 10ca1d35a..003b42d52 100644 --- a/code/LineSplitter.h +++ b/code/LineSplitter.h @@ -69,27 +69,23 @@ for(LineSplitter splitter(stream);splitter;++splitter) { std::cout << "Current line is: " << splitter.get_index() << std::endl; } -@endcode */ +@endcode +*/ // ------------------------------------------------------------------------------------------------ -class LineSplitter -{ +class LineSplitter { public: - typedef size_t line_idx; -public: - // ----------------------------------------- /** construct from existing stream reader note: trim is *always* assumed true if skyp_empty_lines==true */ LineSplitter(StreamReaderLE& stream, bool skip_empty_lines = true, bool trim = true) - : idx( 0 ) - , stream(stream) - , swallow() - , skip_empty_lines(skip_empty_lines) - , trim(trim) - { + : idx( 0 ) + , stream(stream) + , swallow() + , skip_empty_lines(skip_empty_lines) + , trim(trim) { cur.reserve(1024); operator++(); diff --git a/code/STEPFile.h b/code/STEPFile.h index 529d4edbd..8a30beb3f 100644 --- a/code/STEPFile.h +++ b/code/STEPFile.h @@ -55,7 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #if _MSC_VER > 1500 || (defined __GNUC___) # define ASSIMP_STEP_USE_UNORDERED_MULTIMAP -# else +#else # define step_unordered_map map # define step_unordered_multimap multimap #endif diff --git a/code/ScaleProcess.cpp b/code/ScaleProcess.cpp new file mode 100644 index 000000000..ea03d3777 --- /dev/null +++ b/code/ScaleProcess.cpp @@ -0,0 +1,93 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#include "ScaleProcess.h" +#include + +namespace Assimp { + +ScaleProcess::ScaleProcess() +: BaseProcess() +, mScale( 1.0f ) { + // empty +} + +ScaleProcess::~ScaleProcess() { + // empty +} + +void ScaleProcess::setScale( ai_real scale ) { + mScale = scale; +} + +ai_real ScaleProcess::getScale() const { + return mScale; +} + +bool ScaleProcess::IsActive( unsigned int pFlags ) const { + return true; +} + +void ScaleProcess::SetupProperties( const Importer* pImp ) { + mScale = pImp->GetPropertyFloat( AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT ); +} + +void ScaleProcess::Execute( aiScene* pScene ) { + if ( nullptr == pScene ) { + return; + } + + if ( nullptr == pScene->mRootNode ) { + return; + } + + for ( unsigned int i = 0; i < pScene->mRootNode->mNumChildren; ++i ) { + aiNode *currentNode = pScene->mRootNode->mChildren[ i ]; + if ( nullptr != currentNode ) { + applyScaling( currentNode ); + } + } +} + +void ScaleProcess::applyScaling( aiNode *currentNode ) { + currentNode->mTransformation = currentNode->mTransformation * mScale; +} + +} // Namespace Assimp diff --git a/code/ScaleProcess.h b/code/ScaleProcess.h new file mode 100644 index 000000000..6a99c97fe --- /dev/null +++ b/code/ScaleProcess.h @@ -0,0 +1,70 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#pragma once + +#include "BaseProcess.h" + +struct aiNode; + +#if (!defined AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT) +# define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f +#endif // !! AI_DEBONE_THRESHOLD + +namespace Assimp { + +class ScaleProcess : public BaseProcess { +public: + ScaleProcess(); + virtual ~ScaleProcess(); + void setScale( ai_real scale ); + ai_real getScale() const; + virtual bool IsActive( unsigned int pFlags ) const; + virtual void SetupProperties( const Importer* pImp ); + virtual void Execute( aiScene* pScene ); + +private: + void applyScaling( aiNode *currentNode ); + +private: + ai_real mScale; +}; + +} // Namespace Assimp diff --git a/include/assimp/config.h.in b/include/assimp/config.h.in index 00fe7b593..29b9d5870 100644 --- a/include/assimp/config.h.in +++ b/include/assimp/config.h.in @@ -933,6 +933,14 @@ enum aiComponent #define AI_CONFIG_EXPORT_XFILE_64BIT "EXPORT_XFILE_64BIT" +/** + * @brief Specifies a gobal key factor for scale, float value + */ +#define AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY "GLOBAL_SCALE_FACTOR" + +#if (!defined AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT) +# define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f +#endif // !! AI_DEBONE_THRESHOLD // ---------- All the Build/Compile-time defines ------------ From d6f5ad66b24d0a356cf9d9f8af0f2324fe12a218 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 29 Oct 2017 20:32:44 +0100 Subject: [PATCH 261/490] closes https://github.com/assimp/assimp/issues/1514: add misisng flag to enable global scaling. --- include/assimp/postprocess.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/include/assimp/postprocess.h b/include/assimp/postprocess.h index b35bc34f5..970df8e8a 100644 --- a/include/assimp/postprocess.h +++ b/include/assimp/postprocess.h @@ -525,7 +525,17 @@ enum aiPostProcessSteps * Use #AI_CONFIG_PP_DB_ALL_OR_NONE if you want bones removed if and * only if all bones within the scene qualify for removal. */ - aiProcess_Debone = 0x4000000 + aiProcess_Debone = 0x4000000, + + // ------------------------------------------------------------------------- + /**


This step will perform a global scale of the model. + * + * Some importers are providing a mechanism to define a scaling unit for the + * model. This post processing step can be used to do so. + * + * Use #AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY to control this. + */ + aiProcess_GlobalScale = 0x8000000 // aiProcess_GenEntityMeshes = 0x100000, // aiProcess_OptimizeAnimations = 0x200000 From bbeb9dd640e42ea7b95766e6d29d7c14b149ed00 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 29 Oct 2017 23:18:37 +0100 Subject: [PATCH 262/490] Use correct lookup if scaling is enabled. --- code/ScaleProcess.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/code/ScaleProcess.cpp b/code/ScaleProcess.cpp index ea03d3777..4c786d7d1 100644 --- a/code/ScaleProcess.cpp +++ b/code/ScaleProcess.cpp @@ -39,13 +39,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ #include "ScaleProcess.h" + #include +#include namespace Assimp { ScaleProcess::ScaleProcess() : BaseProcess() -, mScale( 1.0f ) { +, mScale( AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT ) { // empty } @@ -62,11 +64,11 @@ ai_real ScaleProcess::getScale() const { } bool ScaleProcess::IsActive( unsigned int pFlags ) const { - return true; + return ( pFlags & aiProcess_GlobalScale ) != 0; } void ScaleProcess::SetupProperties( const Importer* pImp ) { - mScale = pImp->GetPropertyFloat( AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT ); + mScale = pImp->GetPropertyFloat( AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, 0 ); } void ScaleProcess::Execute( aiScene* pScene ) { @@ -87,7 +89,9 @@ void ScaleProcess::Execute( aiScene* pScene ) { } void ScaleProcess::applyScaling( aiNode *currentNode ) { - currentNode->mTransformation = currentNode->mTransformation * mScale; + if ( nullptr != currentNode ) { + currentNode->mTransformation = currentNode->mTransformation * mScale; + } } } // Namespace Assimp From aca8f068d048275db0ac5ec3e8a98118110fd0cf Mon Sep 17 00:00:00 2001 From: Victor NG Date: Mon, 30 Oct 2017 16:29:57 +0800 Subject: [PATCH 263/490] Update ColladaExporter.cpp --- code/ColladaExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ColladaExporter.cpp b/code/ColladaExporter.cpp index 52562a9f7..dd3a9ed1d 100644 --- a/code/ColladaExporter.cpp +++ b/code/ColladaExporter.cpp @@ -1314,7 +1314,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) { std::vector names; - for (int i = 0; i < nodeAnim->mNumPositionKeys; ++i) { + for ( size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) { if ( nodeAnim->mPreState == aiAnimBehaviour_DEFAULT || nodeAnim->mPreState == aiAnimBehaviour_LINEAR || nodeAnim->mPreState == aiAnimBehaviour_REPEAT From 711050de8a031c05d41aa19035de2a4ece94a0cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20J=C3=B8rgen=20Solberg?= Date: Mon, 30 Oct 2017 19:08:51 +0100 Subject: [PATCH 264/490] fix frame pointer arithmetic --- code/MD2Loader.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/code/MD2Loader.cpp b/code/MD2Loader.cpp index cb494d5b2..f0a5432bf 100644 --- a/code/MD2Loader.cpp +++ b/code/MD2Loader.cpp @@ -274,11 +274,9 @@ void MD2Importer::InternReadFile( const std::string& pFile, aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh(); pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; - // navigate to the begin of the frame data - BE_NCONST MD2::Frame* pcFrame = (BE_NCONST MD2::Frame*) ((uint8_t*) - m_pcHeader + m_pcHeader->offsetFrames); - - pcFrame += configFrameID; + // navigate to the begin of the current frame data + BE_NCONST MD2::Frame* pcFrame = (BE_NCONST MD2::Frame*) ((uint8_t*) + m_pcHeader + m_pcHeader->offsetFrames + (m_pcHeader->frameSize * configFrameID)); // navigate to the begin of the triangle data MD2::Triangle* pcTriangles = (MD2::Triangle*) ((uint8_t*) From ae020281e200f11ee5681d8907053a4c955e81e8 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 31 Oct 2017 12:24:32 +0100 Subject: [PATCH 265/490] Add unittest --- code/BlenderDNA.inl | 2 +- code/RemoveVCProcess.h | 4 +--- code/ScaleProcess.cpp | 11 ++++++++--- code/ScaleProcess.h | 19 ++++++++++++++++++- test/CMakeLists.txt | 12 +++++++++--- test/unit/TestModelFactory.h | 8 +++++++- 6 files changed, 44 insertions(+), 12 deletions(-) diff --git a/code/BlenderDNA.inl b/code/BlenderDNA.inl index 8b669180c..c65ea81bc 100644 --- a/code/BlenderDNA.inl +++ b/code/BlenderDNA.inl @@ -585,7 +585,7 @@ template <> inline void Structure :: Convert (int& dest,const FileDataba } // ------------------------------------------------------------------------------------------------ -template <> inline void Structure :: Convert (short& dest,const FileDatabase& db) const +template<> inline void Structure :: Convert (short& dest,const FileDatabase& db) const { // automatic rescaling from short to float and vice versa (seems to be used by normals) if (name == "float") { diff --git a/code/RemoveVCProcess.h b/code/RemoveVCProcess.h index 5735bf419..d7d45e73e 100644 --- a/code/RemoveVCProcess.h +++ b/code/RemoveVCProcess.h @@ -54,8 +54,7 @@ namespace Assimp { /** RemoveVCProcess: Class to exclude specific parts of the data structure * from further processing by removing them, */ -class ASSIMP_API RemoveVCProcess : public BaseProcess -{ +class ASSIMP_API RemoveVCProcess : public BaseProcess { public: /// The default class constructor. RemoveVCProcess(); @@ -63,7 +62,6 @@ public: /// The class destructor. ~RemoveVCProcess(); -public: // ------------------------------------------------------------------- /** Returns whether the processing step is present in the given flag field. * @param pFlags The processing flags the importer was called with. A bitwise diff --git a/code/ScaleProcess.cpp b/code/ScaleProcess.cpp index 4c786d7d1..a0158d220 100644 --- a/code/ScaleProcess.cpp +++ b/code/ScaleProcess.cpp @@ -80,10 +80,15 @@ void ScaleProcess::Execute( aiScene* pScene ) { return; } - for ( unsigned int i = 0; i < pScene->mRootNode->mNumChildren; ++i ) { - aiNode *currentNode = pScene->mRootNode->mChildren[ i ]; + traverseNodes( pScene->mRootNode ); +} + +void ScaleProcess::traverseNodes( aiNode *node ) { + applyScaling( node ); + for ( unsigned int i = 0; i < node->mNumChildren; ++i ) { + aiNode *currentNode = currentNode->mChildren[ i ]; if ( nullptr != currentNode ) { - applyScaling( currentNode ); + traverseNodes( currentNode ); } } } diff --git a/code/ScaleProcess.h b/code/ScaleProcess.h index 6a99c97fe..7eb91fd5c 100644 --- a/code/ScaleProcess.h +++ b/code/ScaleProcess.h @@ -50,17 +50,34 @@ struct aiNode; namespace Assimp { -class ScaleProcess : public BaseProcess { +// --------------------------------------------------------------------------- +/** ScaleProcess: Class to rescale the whole model. +*/ +class ASSIMP_API ScaleProcess : public BaseProcess { public: + /// The default class constructor. ScaleProcess(); + + /// The class destructor. virtual ~ScaleProcess(); + + /// Will set the scale manually. void setScale( ai_real scale ); + + /// Returns the current scaling value. ai_real getScale() const; + + /// Overwritten, @see BaseProcess virtual bool IsActive( unsigned int pFlags ) const; + + /// Overwritten, @see BaseProcess virtual void SetupProperties( const Importer* pImp ); + + /// Overwritten, @see BaseProcess virtual void Execute( aiScene* pScene ); private: + void traverseNodes( aiNode *currentNode ); void applyScaling( aiNode *currentNode ); private: diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3520f9782..62270b935 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -114,8 +114,6 @@ SET( TEST_SRCS unit/utPMXImporter.cpp unit/utRemoveComments.cpp unit/utRemoveComponent.cpp - unit/utRemoveRedundantMaterials.cpp - unit/utRemoveVCProcess.cpp unit/utScenePreprocessor.cpp unit/utSceneCombiner.cpp unit/utSharedPPData.cpp @@ -135,8 +133,15 @@ SET( TEST_SRCS unit/utQ3DImportExport.cpp unit/utProfiler.cpp ) +SET( POST_PROCESSES + unit/utRemoveRedundantMaterials.cpp + unit/utRemoveVCProcess.cpp + unit/utScaleProcess.cpp + unit/utJoinVertices.cpp +) -SOURCE_GROUP( tests FILES ${TEST_SRCS} ) +SOURCE_GROUP( tests FILES ${TEST_SRCS} ) +SOURCE_GROUP( tests/PostProcess FILES ${POST_PROCESSES}) add_executable( unit ../contrib/gtest/src/gtest-all.cc @@ -144,6 +149,7 @@ add_executable( unit unit/Main.cpp ../code/Version.cpp ${TEST_SRCS} + ${POST_PROCESSES} ) add_definitions(-DASSIMP_TEST_MODELS_DIR="${CMAKE_CURRENT_LIST_DIR}/models") diff --git a/test/unit/TestModelFactory.h b/test/unit/TestModelFactory.h index ca070890d..950374112 100644 --- a/test/unit/TestModelFactory.h +++ b/test/unit/TestModelFactory.h @@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "UnitTestPCH.h" #include +#include #include namespace Assimp { @@ -57,7 +58,7 @@ public: // empty } - static aiScene *createDefaultTestModel( float &opacity ) { + static aiScene *createDefaultTestModel( float &opacity ) { aiScene *scene( new aiScene ); scene->mNumMaterials = 1; scene->mMaterials = new aiMaterial*[scene->mNumMaterials]; @@ -93,6 +94,11 @@ public: return scene; } + + static void releaseDefaultTestModel( aiScene **scene ) { + delete *scene; + *scene = nullptr; + } }; } From 64ee21024b069137f650225c2cace752591eb9e7 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 31 Oct 2017 13:07:07 +0100 Subject: [PATCH 266/490] Add missing file. --- code/ScaleProcess.cpp | 4 +- test/unit/utScaleProcess.cpp | 85 ++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 test/unit/utScaleProcess.cpp diff --git a/code/ScaleProcess.cpp b/code/ScaleProcess.cpp index a0158d220..4556df782 100644 --- a/code/ScaleProcess.cpp +++ b/code/ScaleProcess.cpp @@ -95,7 +95,9 @@ void ScaleProcess::traverseNodes( aiNode *node ) { void ScaleProcess::applyScaling( aiNode *currentNode ) { if ( nullptr != currentNode ) { - currentNode->mTransformation = currentNode->mTransformation * mScale; + currentNode->mTransformation.a1 = currentNode->mTransformation.a1 * mScale; + currentNode->mTransformation.b2 = currentNode->mTransformation.b2 * mScale; + currentNode->mTransformation.c3 = currentNode->mTransformation.c3 * mScale; } } diff --git a/test/unit/utScaleProcess.cpp b/test/unit/utScaleProcess.cpp new file mode 100644 index 000000000..7ab44dd15 --- /dev/null +++ b/test/unit/utScaleProcess.cpp @@ -0,0 +1,85 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +#include "UnitTestPCH.h" +#include "ScaleProcess.h" +#include "TestModelFactory.h" + +namespace Assimp { +namespace UnitTest { + +class utScaleProcess : public ::testing::Test { + // empty +}; + +TEST_F( utScaleProcess, createTest ) { + bool ok = true; + try { + ScaleProcess process; + } catch ( ... ) { + ok = false; + } + EXPECT_TRUE( ok ); +} + +TEST_F( utScaleProcess, accessScaleTest ) { + ScaleProcess process; + EXPECT_FLOAT_EQ( AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT, process.getScale() ); + + process.setScale( 2.0f ); + EXPECT_FLOAT_EQ( 2.0f, process.getScale() ); +} + +TEST_F( utScaleProcess, rescaleModelTest ) { + float opacity; + aiScene *testScene = TestModelFacttory::createDefaultTestModel( opacity ); + ai_real v1 = testScene->mRootNode->mTransformation.a1; + ScaleProcess process; + process.setScale( 10.0f ); + process.Execute( testScene ); + ai_real v2 = testScene->mRootNode->mTransformation.a1; + const ai_real scale = v2 / v1; + EXPECT_FLOAT_EQ( scale, 10.0f ); + TestModelFacttory::releaseDefaultTestModel( &testScene ); +} + +} // Namespace UnitTest +} // Namespace Assimp From af4556d56963f61808f7273480d607e4f46737d5 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 31 Oct 2017 13:08:24 +0100 Subject: [PATCH 267/490] only scale the root node because this will rescale all children nodes as well. --- code/ScaleProcess.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/code/ScaleProcess.cpp b/code/ScaleProcess.cpp index 4556df782..ada978e82 100644 --- a/code/ScaleProcess.cpp +++ b/code/ScaleProcess.cpp @@ -85,12 +85,13 @@ void ScaleProcess::Execute( aiScene* pScene ) { void ScaleProcess::traverseNodes( aiNode *node ) { applyScaling( node ); - for ( unsigned int i = 0; i < node->mNumChildren; ++i ) { + + /*for ( unsigned int i = 0; i < node->mNumChildren; ++i ) { aiNode *currentNode = currentNode->mChildren[ i ]; if ( nullptr != currentNode ) { traverseNodes( currentNode ); } - } + }*/ } void ScaleProcess::applyScaling( aiNode *currentNode ) { From 8be196f77de94e42456b2fa39511b037181e0a77 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 31 Oct 2017 14:03:03 +0100 Subject: [PATCH 268/490] closes https://github.com/assimp/assimp/issues/1490 : fix invalid access to mesh array when the array is empty. --- code/BlenderModifier.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/BlenderModifier.cpp b/code/BlenderModifier.cpp index a1ccba5f4..348df1f48 100644 --- a/code/BlenderModifier.cpp +++ b/code/BlenderModifier.cpp @@ -310,7 +310,9 @@ void BlenderModifier_Subdivision :: DoIt(aiNode& out, ConversionData& conv_data std::unique_ptr subd(Subdivider::Create(algo)); ai_assert(subd); - + if ( conv_data.meshes->empty() ) { + return; + } aiMesh** const meshes = &conv_data.meshes[conv_data.meshes->size() - out.mNumMeshes]; std::unique_ptr tempmeshes(new aiMesh*[out.mNumMeshes]()); From cc4531459f3e131623d93dfe62ccc25115f0ca52 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Tue, 31 Oct 2017 15:14:21 -0400 Subject: [PATCH 269/490] Set mNumUVComponents to 0 when deleting texture coordinate sets --- code/FindInvalidDataProcess.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/code/FindInvalidDataProcess.cpp b/code/FindInvalidDataProcess.cpp index 77915bc4e..60b690844 100644 --- a/code/FindInvalidDataProcess.cpp +++ b/code/FindInvalidDataProcess.cpp @@ -360,10 +360,13 @@ int FindInvalidDataProcess::ProcessMesh (aiMesh* pMesh) // process texture coordinates for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS && pMesh->mTextureCoords[i];++i) { if (ProcessArray(pMesh->mTextureCoords[i],pMesh->mNumVertices,"uvcoords",dirtyMask)) { + pMesh->mNumUVComponents[i] = 0; // delete all subsequent texture coordinate sets. - for (unsigned int a = i+1; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) { - delete[] pMesh->mTextureCoords[a]; pMesh->mTextureCoords[a] = NULL; + for (unsigned int a = i + 1; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) { + delete[] pMesh->mTextureCoords[a]; + pMesh->mTextureCoords[a] = NULL; + pMesh->mNumUVComponents[a] = 0; } ret = true; } From e52e44ea07375027db56a5384e342969853bbcaa Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Tue, 31 Oct 2017 15:18:08 -0400 Subject: [PATCH 270/490] Formatting --- code/FindInvalidDataProcess.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/code/FindInvalidDataProcess.cpp b/code/FindInvalidDataProcess.cpp index 60b690844..a6cb037b2 100644 --- a/code/FindInvalidDataProcess.cpp +++ b/code/FindInvalidDataProcess.cpp @@ -339,27 +339,28 @@ void FindInvalidDataProcess::ProcessAnimationChannel (aiNodeAnim* anim) int FindInvalidDataProcess::ProcessMesh (aiMesh* pMesh) { bool ret = false; - std::vector dirtyMask(pMesh->mNumVertices,(pMesh->mNumFaces ? true : false)); + std::vector dirtyMask(pMesh->mNumVertices, pMesh->mNumFaces); // Ignore elements that are not referenced by vertices. // (they are, for example, caused by the FindDegenerates step) - for (unsigned int m = 0; m < pMesh->mNumFaces;++m) { + for (unsigned int m = 0; m < pMesh->mNumFaces; ++m) { const aiFace& f = pMesh->mFaces[m]; - for (unsigned int i = 0; i < f.mNumIndices;++i) { + for (unsigned int i = 0; i < f.mNumIndices; ++i) { dirtyMask[f.mIndices[i]] = false; } } // Process vertex positions - if(pMesh->mVertices && ProcessArray(pMesh->mVertices,pMesh->mNumVertices,"positions",dirtyMask)) { + if (pMesh->mVertices && ProcessArray(pMesh->mVertices, pMesh->mNumVertices, "positions", dirtyMask)) { DefaultLogger::get()->error("Deleting mesh: Unable to continue without vertex positions"); + return 2; } // process texture coordinates - for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS && pMesh->mTextureCoords[i];++i) { - if (ProcessArray(pMesh->mTextureCoords[i],pMesh->mNumVertices,"uvcoords",dirtyMask)) { + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS && pMesh->mTextureCoords[i]; ++i) { + if (ProcessArray(pMesh->mTextureCoords[i], pMesh->mNumVertices, "uvcoords", dirtyMask)) { pMesh->mNumUVComponents[i] = 0; // delete all subsequent texture coordinate sets. @@ -368,6 +369,7 @@ int FindInvalidDataProcess::ProcessMesh (aiMesh* pMesh) pMesh->mTextureCoords[a] = NULL; pMesh->mNumUVComponents[a] = 0; } + ret = true; } } From bf9d3194898d58c89e95799087ae65038f5b35b1 Mon Sep 17 00:00:00 2001 From: Daeyun Shin Date: Tue, 31 Oct 2017 15:43:39 -0700 Subject: [PATCH 271/490] Update helper.py Search for libassimp.so in LD_LIBRARY_PATH if available. --- port/PyAssimp/pyassimp/helper.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/port/PyAssimp/pyassimp/helper.py b/port/PyAssimp/pyassimp/helper.py index 4fa01d51d..85bb53add 100644 --- a/port/PyAssimp/pyassimp/helper.py +++ b/port/PyAssimp/pyassimp/helper.py @@ -30,6 +30,9 @@ if os.name=='posix': additional_dirs.append('/usr/lib/x86_64-linux-gnu') additional_dirs.append('/usr/local/lib/') + if 'LD_LIBRARY_PATH' in os.environ: + additional_dirs.extend([item for item in os.environ['LD_LIBRARY_PATH'].split(':') if item]) + # check if running from anaconda. if "conda" or "continuum" in sys.version.lower(): cur_path = get_python_lib() From 5b76a31485bcb28363132f2bce8c8acc4506b0e0 Mon Sep 17 00:00:00 2001 From: Thomas Lemaire Date: Thu, 2 Nov 2017 11:13:52 +0100 Subject: [PATCH 272/490] fix trivial warnings mainly unused parameter and unused function some parameters are indeed used in a debug built, I used the (void)(param) trick warnings reported by clang 4 --- code/3DSExporter.cpp | 2 +- code/AMFImporter_Postprocess.cpp | 2 +- code/AssbinExporter.cpp | 2 +- code/AssbinLoader.cpp | 11 ++++++++++ code/AssxmlExporter.cpp | 2 +- code/BlenderIntermediate.h | 2 +- code/ColladaExporter.cpp | 2 +- code/ColladaParser.cpp | 3 +-- code/D3MFImporter.cpp | 2 +- code/D3MFOpcPackage.cpp | 1 + code/Exporter.cpp | 2 +- code/FBXConverter.cpp | 2 ++ code/FBXParser.cpp | 1 + code/FIReader.cpp | 8 +++---- code/FindInvalidDataProcess.cpp | 4 ++-- code/IFCCurve.cpp | 1 + code/IFCReaderGen1.cpp | 6 ++--- code/IRRLoader.cpp | 2 +- code/MMDImporter.cpp | 2 +- code/MMDPmxParser.cpp | 2 +- code/MMDVmdParser.h | 2 +- code/ObjExporter.cpp | 8 +++---- code/OpenGEXExporter.cpp | 2 +- code/OpenGEXImporter.cpp | 22 +++++++++---------- code/PlyExporter.cpp | 4 ++-- code/Q3BSPZipArchive.cpp | 1 + code/SIBImporter.cpp | 2 +- code/STLExporter.cpp | 4 ++-- code/X3DExporter.cpp | 2 +- code/X3DImporter_Metadata.cpp | 2 +- code/glTF2Asset.h | 2 +- code/glTF2Asset.inl | 10 ++++----- code/glTF2AssetWriter.inl | 8 +++---- code/glTF2Exporter.cpp | 2 +- code/glTF2Importer.cpp | 8 ++++--- code/glTFAsset.h | 2 +- code/glTFAsset.inl | 8 +++---- code/glTFAssetWriter.inl | 10 ++++----- code/glTFImporter.cpp | 4 +++- contrib/Open3DGC/o3dgcTriangleListEncoder.inl | 2 +- contrib/openddlparser/code/DDLNode.cpp | 2 +- contrib/openddlparser/code/Value.cpp | 2 +- .../include/openddlparser/OpenDDLParser.h | 2 +- include/assimp/Exporter.hpp | 2 +- 44 files changed, 96 insertions(+), 76 deletions(-) diff --git a/code/3DSExporter.cpp b/code/3DSExporter.cpp index 642f7dc57..7b937d878 100644 --- a/code/3DSExporter.cpp +++ b/code/3DSExporter.cpp @@ -151,7 +151,7 @@ namespace { // ------------------------------------------------------------------------------------------------ // Worker function for exporting a scene to 3DS. Prototyped and registered in Exporter.cpp -void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties) +void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) { std::shared_ptr outfile (pIOSystem->Open(pFile, "wb")); if(!outfile) { diff --git a/code/AMFImporter_Postprocess.cpp b/code/AMFImporter_Postprocess.cpp index 789a11eb8..0e639026c 100644 --- a/code/AMFImporter_Postprocess.cpp +++ b/code/AMFImporter_Postprocess.cpp @@ -60,7 +60,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { -aiColor4D AMFImporter::SPP_Material::GetColor(const float pX, const float pY, const float pZ) const +aiColor4D AMFImporter::SPP_Material::GetColor(const float /*pX*/, const float /*pY*/, const float /*pZ*/) const { aiColor4D tcol; diff --git a/code/AssbinExporter.cpp b/code/AssbinExporter.cpp index a6fbcd8f7..6bee6d6d1 100644 --- a/code/AssbinExporter.cpp +++ b/code/AssbinExporter.cpp @@ -810,7 +810,7 @@ inline size_t WriteArray(IOStream * stream, const T* in, unsigned int size) } }; -void ExportSceneAssbin(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties) +void ExportSceneAssbin(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) { AssbinExport exporter; exporter.WriteBinaryDump( pFile, pIOSystem, pScene ); diff --git a/code/AssbinLoader.cpp b/code/AssbinLoader.cpp index f418638e1..a7044c119 100644 --- a/code/AssbinLoader.cpp +++ b/code/AssbinLoader.cpp @@ -200,6 +200,7 @@ template void ReadBounds( IOStream * stream, T* /*p*/, unsigned int void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** node, aiNode* parent ) { uint32_t chunkID = Read(stream); + (void)(chunkID); ai_assert(chunkID == ASSBIN_CHUNK_AINODE); /*uint32_t size =*/ Read(stream); @@ -274,6 +275,7 @@ void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** node, aiNode* p void AssbinImporter::ReadBinaryBone( IOStream * stream, aiBone* b ) { uint32_t chunkID = Read(stream); + (void)(chunkID); ai_assert(chunkID == ASSBIN_CHUNK_AIBONE); /*uint32_t size =*/ Read(stream); @@ -298,6 +300,7 @@ void AssbinImporter::ReadBinaryBone( IOStream * stream, aiBone* b ) void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh ) { uint32_t chunkID = Read(stream); + (void)(chunkID); ai_assert(chunkID == ASSBIN_CHUNK_AIMESH); /*uint32_t size =*/ Read(stream); @@ -423,6 +426,7 @@ void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh ) void AssbinImporter::ReadBinaryMaterialProperty(IOStream * stream, aiMaterialProperty* prop) { uint32_t chunkID = Read(stream); + (void)(chunkID); ai_assert(chunkID == ASSBIN_CHUNK_AIMATERIALPROPERTY); /*uint32_t size =*/ Read(stream); @@ -440,6 +444,7 @@ void AssbinImporter::ReadBinaryMaterialProperty(IOStream * stream, aiMaterialPro void AssbinImporter::ReadBinaryMaterial(IOStream * stream, aiMaterial* mat) { uint32_t chunkID = Read(stream); + (void)(chunkID); ai_assert(chunkID == ASSBIN_CHUNK_AIMATERIAL); /*uint32_t size =*/ Read(stream); @@ -462,6 +467,7 @@ void AssbinImporter::ReadBinaryMaterial(IOStream * stream, aiMaterial* mat) void AssbinImporter::ReadBinaryNodeAnim(IOStream * stream, aiNodeAnim* nd) { uint32_t chunkID = Read(stream); + (void)(chunkID); ai_assert(chunkID == ASSBIN_CHUNK_AINODEANIM); /*uint32_t size =*/ Read(stream); @@ -511,6 +517,7 @@ void AssbinImporter::ReadBinaryNodeAnim(IOStream * stream, aiNodeAnim* nd) void AssbinImporter::ReadBinaryAnim( IOStream * stream, aiAnimation* anim ) { uint32_t chunkID = Read(stream); + (void)(chunkID); ai_assert(chunkID == ASSBIN_CHUNK_AIANIMATION); /*uint32_t size =*/ Read(stream); @@ -532,6 +539,7 @@ void AssbinImporter::ReadBinaryAnim( IOStream * stream, aiAnimation* anim ) void AssbinImporter::ReadBinaryTexture(IOStream * stream, aiTexture* tex) { uint32_t chunkID = Read(stream); + (void)(chunkID); ai_assert(chunkID == ASSBIN_CHUNK_AITEXTURE); /*uint32_t size =*/ Read(stream); @@ -556,6 +564,7 @@ void AssbinImporter::ReadBinaryTexture(IOStream * stream, aiTexture* tex) void AssbinImporter::ReadBinaryLight( IOStream * stream, aiLight* l ) { uint32_t chunkID = Read(stream); + (void)(chunkID); ai_assert(chunkID == ASSBIN_CHUNK_AILIGHT); /*uint32_t size =*/ Read(stream); @@ -583,6 +592,7 @@ void AssbinImporter::ReadBinaryLight( IOStream * stream, aiLight* l ) void AssbinImporter::ReadBinaryCamera( IOStream * stream, aiCamera* cam ) { uint32_t chunkID = Read(stream); + (void)(chunkID); ai_assert(chunkID == ASSBIN_CHUNK_AICAMERA); /*uint32_t size =*/ Read(stream); @@ -599,6 +609,7 @@ void AssbinImporter::ReadBinaryCamera( IOStream * stream, aiCamera* cam ) void AssbinImporter::ReadBinaryScene( IOStream * stream, aiScene* scene ) { uint32_t chunkID = Read(stream); + (void)(chunkID); ai_assert(chunkID == ASSBIN_CHUNK_AISCENE); /*uint32_t size =*/ Read(stream); diff --git a/code/AssxmlExporter.cpp b/code/AssxmlExporter.cpp index 8019232c0..90ed66701 100644 --- a/code/AssxmlExporter.cpp +++ b/code/AssxmlExporter.cpp @@ -631,7 +631,7 @@ void WriteDump(const aiScene* scene, IOStream* io, bool shortened) { } // end of namespace AssxmlExport -void ExportSceneAssxml(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties) +void ExportSceneAssxml(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) { IOStream * out = pIOSystem->Open( pFile, "wt" ); if (!out) return; diff --git a/code/BlenderIntermediate.h b/code/BlenderIntermediate.h index 0fc9cdafc..c6f9cba1e 100644 --- a/code/BlenderIntermediate.h +++ b/code/BlenderIntermediate.h @@ -110,7 +110,7 @@ namespace Blender { void operator= (const TempArray&) { } - TempArray(const TempArray& arr) { + TempArray(const TempArray& /*arr*/) { } private: diff --git a/code/ColladaExporter.cpp b/code/ColladaExporter.cpp index 53ee07388..d886a2143 100644 --- a/code/ColladaExporter.cpp +++ b/code/ColladaExporter.cpp @@ -68,7 +68,7 @@ namespace Assimp // ------------------------------------------------------------------------------------------------ // Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp -void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties) +void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) { std::string path = DefaultIOSystem::absolutePath(std::string(pFile)); std::string file = DefaultIOSystem::completeBaseName(std::string(pFile)); diff --git a/code/ColladaParser.cpp b/code/ColladaParser.cpp index 7b6592351..5ff581b91 100644 --- a/code/ColladaParser.cpp +++ b/code/ColladaParser.cpp @@ -2469,8 +2469,7 @@ void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t n size_t baseOffset = currentPrimitive * numOffsets * numPoints + currentVertex * numOffsets; // don't overrun the boundaries of the index list - size_t maxIndexRequested = baseOffset + numOffsets - 1; - ai_assert(maxIndexRequested < indices.size()); + ai_assert((baseOffset + numOffsets - 1) < indices.size()); // extract per-vertex channels using the global per-vertex offset for (std::vector::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it) diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index e97e22859..6d61a0669 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -344,7 +344,7 @@ bool D3MFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool return false; } -void D3MFImporter::SetupProperties(const Importer *pImp) +void D3MFImporter::SetupProperties(const Importer */*pImp*/) { } diff --git a/code/D3MFOpcPackage.cpp b/code/D3MFOpcPackage.cpp index b6b1ba170..03c39a29d 100644 --- a/code/D3MFOpcPackage.cpp +++ b/code/D3MFOpcPackage.cpp @@ -379,6 +379,7 @@ IOStream *D3MFZipArchive::Open(const char* pFile, const char* /*pMode*/) { // ------------------------------------------------------------------------------------------------ // Close a filestream. void D3MFZipArchive::Close(IOStream *pFile) { + (void)(pFile); ai_assert(pFile != NULL); // We don't do anything in case the file would be opened again in the future diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 1e2d7cbf1..b83a540b9 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -241,7 +241,7 @@ bool Exporter::IsDefaultIOHandler() const { // ------------------------------------------------------------------------------------------------ const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const char* pFormatId, - unsigned int, const ExportProperties* pProperties ) { + unsigned int, const ExportProperties* /*pProperties*/ ) { if (pimpl->blob) { delete pimpl->blob; pimpl->blob = NULL; diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index 1a80fa4f5..a1121cc40 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -2429,6 +2429,7 @@ void Converter::ConvertAnimationStack( const AnimationStack& st ) anim->mTicksPerSecond = anim_fps; } +#ifdef ASSIMP_BUILD_DEBUG // ------------------------------------------------------------------------------------------------ // sanity check whether the input is ok static void validateAnimCurveNodes( const std::vector& curves, @@ -2446,6 +2447,7 @@ static void validateAnimCurveNodes( const std::vector } } } +#endif // ASSIMP_BUILD_DEBUG // ------------------------------------------------------------------------------------------------ void Converter::GenerateNodeAnimations( std::vector& node_anims, diff --git a/code/FBXParser.cpp b/code/FBXParser.cpp index b5661caf8..d4d06567b 100644 --- a/code/FBXParser.cpp +++ b/code/FBXParser.cpp @@ -103,6 +103,7 @@ namespace { T SafeParse(const char* data, const char* end) { // Actual size validation happens during Tokenization so // this is valid as an assertion. + (void)(end); ai_assert(static_cast(end - data) >= sizeof(T)); T result = static_cast(0); ::memcpy(&result, data, sizeof(T)); diff --git a/code/FIReader.cpp b/code/FIReader.cpp index 95b22a1b8..fdf6b8a83 100755 --- a/code/FIReader.cpp +++ b/code/FIReader.cpp @@ -1776,17 +1776,17 @@ public: return reader->getParserFormat(); } - virtual std::shared_ptr getAttributeEncodedValue(int idx) const /*override*/ { + virtual std::shared_ptr getAttributeEncodedValue(int /*idx*/) const /*override*/ { return nullptr; } - virtual std::shared_ptr getAttributeEncodedValue(const char* name) const /*override*/ { + virtual std::shared_ptr getAttributeEncodedValue(const char* /*name*/) const /*override*/ { return nullptr; } - virtual void registerDecoder(const std::string &algorithmUri, std::unique_ptr decoder) /*override*/ {} + virtual void registerDecoder(const std::string &/*algorithmUri*/, std::unique_ptr /*decoder*/) /*override*/ {} - virtual void registerVocabulary(const std::string &vocabularyUri, const FIVocabulary *vocabulary) /*override*/ {} + virtual void registerVocabulary(const std::string &/*vocabularyUri*/, const FIVocabulary */*vocabulary*/) /*override*/ {} private: diff --git a/code/FindInvalidDataProcess.cpp b/code/FindInvalidDataProcess.cpp index 77915bc4e..b4271a388 100644 --- a/code/FindInvalidDataProcess.cpp +++ b/code/FindInvalidDataProcess.cpp @@ -169,8 +169,8 @@ void FindInvalidDataProcess::Execute( aiScene* pScene) // ------------------------------------------------------------------------------------------------ template -inline const char* ValidateArrayContents(const T* arr, unsigned int size, - const std::vector& dirtyMask, bool mayBeIdentical = false, bool mayBeZero = true) +inline const char* ValidateArrayContents(const T* /*arr*/, unsigned int /*size*/, + const std::vector& /*dirtyMask*/, bool /*mayBeIdentical = false*/, bool /*mayBeZero = true*/) { return NULL; } diff --git a/code/IFCCurve.cpp b/code/IFCCurve.cpp index 176fe3c13..9e1354f93 100644 --- a/code/IFCCurve.cpp +++ b/code/IFCCurve.cpp @@ -564,6 +564,7 @@ IfcFloat Curve :: GetParametricRangeDelta() const // ------------------------------------------------------------------------------------------------ size_t Curve :: EstimateSampleCount(IfcFloat a, IfcFloat b) const { + (void)(a); (void)(b); ai_assert(InRange(a) && InRange(b)); // arbitrary default value, deriving classes should supply better suited values diff --git a/code/IFCReaderGen1.cpp b/code/IFCReaderGen1.cpp index d334e8322..bd526d454 100644 --- a/code/IFCReaderGen1.cpp +++ b/code/IFCReaderGen1.cpp @@ -1045,7 +1045,7 @@ void IFC::GetSchema(EXPRESS::ConversionSchema& out) namespace STEP { // ----------------------------------------------------------------------------------------------------------- -template <> size_t GenericFill(const STEP::DB& db, const LIST& params, NotImplemented* in) +template <> size_t GenericFill(const STEP::DB& /*db*/, const LIST& /*params*/, NotImplemented* /*in*/) { return 0; } @@ -1253,7 +1253,7 @@ template <> size_t GenericFill(const DB& db, const LIST& return base; } // ----------------------------------------------------------------------------------------------------------- -template <> size_t GenericFill(const DB& db, const LIST& params, IfcRepresentationItem* in) +template <> size_t GenericFill(const DB& /*db*/, const LIST& /*params*/, IfcRepresentationItem* /*in*/) { size_t base = 0; return base; @@ -1715,7 +1715,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I return base; } // ----------------------------------------------------------------------------------------------------------- -template <> size_t GenericFill(const DB& db, const LIST& params, IfcObjectPlacement* in) +template <> size_t GenericFill(const DB& /*db*/, const LIST& /*params*/, IfcObjectPlacement* /*in*/) { size_t base = 0; return base; diff --git a/code/IRRLoader.cpp b/code/IRRLoader.cpp index 8cc4acaad..5113ab70d 100644 --- a/code/IRRLoader.cpp +++ b/code/IRRLoader.cpp @@ -394,7 +394,7 @@ void IRRImporter::ComputeAnimations(Node* root, aiNode* real, std::vectorread((char*) &this->is_near, sizeof(uint8_t)); } - void PmxSoftBody::Read(std::istream *stream, PmxSetting *setting) + void PmxSoftBody::Read(std::istream */*stream*/, PmxSetting */*setting*/) { // 未実装 std::cerr << "Not Implemented Exception" << std::endl; diff --git a/code/MMDVmdParser.h b/code/MMDVmdParser.h index 778ee1161..43e7a923a 100644 --- a/code/MMDVmdParser.h +++ b/code/MMDVmdParser.h @@ -302,7 +302,7 @@ namespace vmd return result; } - bool SaveToFile(const std::u16string& filename) + bool SaveToFile(const std::u16string& /*filename*/) { // TODO: How to adapt u16string to string? /* diff --git a/code/ObjExporter.cpp b/code/ObjExporter.cpp index 692eac0e6..3da002080 100644 --- a/code/ObjExporter.cpp +++ b/code/ObjExporter.cpp @@ -58,7 +58,7 @@ namespace Assimp { // ------------------------------------------------------------------------------------------------ // Worker function for exporting a scene to Wavefront OBJ. Prototyped and registered in Exporter.cpp -void ExportSceneObj(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties) { +void ExportSceneObj(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) { // invoke the exporter ObjExporter exporter(pFile, pScene); @@ -94,7 +94,7 @@ ObjExporter::ObjExporter(const char* _filename, const aiScene* pScene) , vp() , vn() , vt() -, vc() +, vc() , mVpMap() , mVnMap() , mVtMap() @@ -193,7 +193,7 @@ void ObjExporter::WriteMaterialFile() if(AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_TRANSPARENT,c)) { mOutputMat << "Tf " << c.r << " " << c.g << " " << c.b << endl; } - + ai_real o; if(AI_SUCCESS == mat->Get(AI_MATKEY_OPACITY,o)) { mOutputMat << "d " << o << endl; @@ -341,7 +341,7 @@ int ObjExporter::colIndexMap::getIndex( const aiColor4D& col ) { colMap[ col ] = mNextIndex; int ret = mNextIndex; mNextIndex++; - + return ret; } diff --git a/code/OpenGEXExporter.cpp b/code/OpenGEXExporter.cpp index ea3c770ec..b3597656b 100644 --- a/code/OpenGEXExporter.cpp +++ b/code/OpenGEXExporter.cpp @@ -51,7 +51,7 @@ OpenGEXExporter::OpenGEXExporter() { OpenGEXExporter::~OpenGEXExporter() { } -bool OpenGEXExporter::exportScene( const char *filename, const aiScene* pScene ) { +bool OpenGEXExporter::exportScene( const char */*filename*/, const aiScene* /*pScene*/ ) { return true; } diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 47e3e3e8c..27d7b3e49 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -431,7 +431,7 @@ void OpenGEXImporter::handleNodes( DDLNode *node, aiScene *pScene ) { } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleMetricNode( DDLNode *node, aiScene *pScene ) { +void OpenGEXImporter::handleMetricNode( DDLNode *node, aiScene */*pScene*/ ) { if( nullptr == node || nullptr == m_ctx ) { return; } @@ -467,7 +467,7 @@ void OpenGEXImporter::handleMetricNode( DDLNode *node, aiScene *pScene ) { } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleNameNode( DDLNode *node, aiScene *pScene ) { +void OpenGEXImporter::handleNameNode( DDLNode *node, aiScene */*pScene*/ ) { if( nullptr == m_currentNode ) { throw DeadlyImportError( "No current node for name." ); return; @@ -512,7 +512,7 @@ static void getRefNames( DDLNode *node, std::vector &names ) { } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleObjectRefNode( DDLNode *node, aiScene *pScene ) { +void OpenGEXImporter::handleObjectRefNode( DDLNode *node, aiScene */*pScene*/ ) { if( nullptr == m_currentNode ) { throw DeadlyImportError( "No parent node for name." ); return; @@ -536,7 +536,7 @@ void OpenGEXImporter::handleObjectRefNode( DDLNode *node, aiScene *pScene ) { } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleMaterialRefNode( ODDLParser::DDLNode *node, aiScene *pScene ) { +void OpenGEXImporter::handleMaterialRefNode( ODDLParser::DDLNode *node, aiScene */*pScene*/ ) { if( nullptr == m_currentNode ) { throw DeadlyImportError( "No parent node for name." ); return; @@ -674,7 +674,7 @@ static void setMatrix( aiNode *node, DataArrayList *transformData ) { } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleTransformNode( ODDLParser::DDLNode *node, aiScene *pScene ) { +void OpenGEXImporter::handleTransformNode( ODDLParser::DDLNode *node, aiScene */*pScene*/ ) { if( nullptr == m_currentNode ) { throw DeadlyImportError( "No parent node for name." ); return; @@ -819,7 +819,7 @@ static void copyColor4DArray( size_t numItems, DataArrayList *vaList, aiColor4D } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleVertexArrayNode( ODDLParser::DDLNode *node, aiScene *pScene ) { +void OpenGEXImporter::handleVertexArrayNode( ODDLParser::DDLNode *node, aiScene */*pScene*/ ) { if( nullptr == node ) { throw DeadlyImportError( "No parent node for name." ); return; @@ -862,7 +862,7 @@ void OpenGEXImporter::handleVertexArrayNode( ODDLParser::DDLNode *node, aiScene } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleIndexArrayNode( ODDLParser::DDLNode *node, aiScene *pScene ) { +void OpenGEXImporter::handleIndexArrayNode( ODDLParser::DDLNode *node, aiScene */*pScene*/ ) { if( nullptr == node ) { throw DeadlyImportError( "No parent node for name." ); return; @@ -1001,7 +1001,7 @@ void OpenGEXImporter::handleMaterialNode( ODDLParser::DDLNode *node, aiScene *pS } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleColorNode( ODDLParser::DDLNode *node, aiScene *pScene ) { +void OpenGEXImporter::handleColorNode( ODDLParser::DDLNode *node, aiScene */*pScene*/ ) { if( nullptr == node ) { return; } @@ -1040,7 +1040,7 @@ void OpenGEXImporter::handleColorNode( ODDLParser::DDLNode *node, aiScene *pScen } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleTextureNode( ODDLParser::DDLNode *node, aiScene *pScene ) { +void OpenGEXImporter::handleTextureNode( ODDLParser::DDLNode *node, aiScene */*pScene*/ ) { if( nullptr == node ) { return; } @@ -1074,7 +1074,7 @@ void OpenGEXImporter::handleTextureNode( ODDLParser::DDLNode *node, aiScene *pSc } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleParamNode( ODDLParser::DDLNode *node, aiScene *pScene ) { +void OpenGEXImporter::handleParamNode( ODDLParser::DDLNode *node, aiScene */*pScene*/ ) { if ( nullptr == node ) { return; } @@ -1103,7 +1103,7 @@ void OpenGEXImporter::handleParamNode( ODDLParser::DDLNode *node, aiScene *pScen } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleAttenNode( ODDLParser::DDLNode *node, aiScene *pScene ) { +void OpenGEXImporter::handleAttenNode( ODDLParser::DDLNode *node, aiScene */*pScene*/ ) { if ( nullptr == node ) { return; } diff --git a/code/PlyExporter.cpp b/code/PlyExporter.cpp index eb0867046..b20c2b328 100644 --- a/code/PlyExporter.cpp +++ b/code/PlyExporter.cpp @@ -65,7 +65,7 @@ template<> const char* type_of(double&) { return "double"; } // ------------------------------------------------------------------------------------------------ // Worker function for exporting a scene to PLY. Prototyped and registered in Exporter.cpp -void ExportScenePly(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties) +void ExportScenePly(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) { // invoke the exporter PlyExporter exporter(pFile, pScene); @@ -83,7 +83,7 @@ void ExportScenePly(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene outfile->Write( exporter.mOutput.str().c_str(), static_cast(exporter.mOutput.tellp()),1); } -void ExportScenePlyBinary(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties) +void ExportScenePlyBinary(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) { // invoke the exporter PlyExporter exporter(pFile, pScene, true); diff --git a/code/Q3BSPZipArchive.cpp b/code/Q3BSPZipArchive.cpp index 7428c04c3..86c966d8e 100644 --- a/code/Q3BSPZipArchive.cpp +++ b/code/Q3BSPZipArchive.cpp @@ -255,6 +255,7 @@ IOStream *Q3BSPZipArchive::Open(const char* pFile, const char* /*pMode*/) { // ------------------------------------------------------------------------------------------------ // Close a filestream. void Q3BSPZipArchive::Close(IOStream *pFile) { + (void)(pFile); ai_assert(pFile != NULL); // We don't do anything in case the file would be opened again in the future diff --git a/code/SIBImporter.cpp b/code/SIBImporter.cpp index bdda3c677..e85c9bdcb 100644 --- a/code/SIBImporter.cpp +++ b/code/SIBImporter.cpp @@ -163,7 +163,7 @@ static aiColor3D ReadColor(StreamReaderLE* stream) return aiColor3D(r, g, b); } -static void UnknownChunk(StreamReaderLE* stream, const SIBChunk& chunk) +static void UnknownChunk(StreamReaderLE* /*stream*/, const SIBChunk& chunk) { char temp[5] = { static_cast(( chunk.Tag>>24 ) & 0xff), diff --git a/code/STLExporter.cpp b/code/STLExporter.cpp index f94debfc9..60b5d8bf3 100644 --- a/code/STLExporter.cpp +++ b/code/STLExporter.cpp @@ -57,7 +57,7 @@ namespace Assimp { // ------------------------------------------------------------------------------------------------ // Worker function for exporting a scene to Stereolithograpy. Prototyped and registered in Exporter.cpp -void ExportSceneSTL(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties) +void ExportSceneSTL(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) { // invoke the exporter STLExporter exporter(pFile, pScene); @@ -74,7 +74,7 @@ void ExportSceneSTL(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene outfile->Write( exporter.mOutput.str().c_str(), static_cast(exporter.mOutput.tellp()),1); } -void ExportSceneSTLBinary(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties) +void ExportSceneSTLBinary(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) { // invoke the exporter STLExporter exporter(pFile, pScene, true); diff --git a/code/X3DExporter.cpp b/code/X3DExporter.cpp index 31a391c47..df688fd79 100644 --- a/code/X3DExporter.cpp +++ b/code/X3DExporter.cpp @@ -692,7 +692,7 @@ bool found = false; return true; } -X3DExporter::X3DExporter(const char* pFileName, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties) +X3DExporter::X3DExporter(const char* pFileName, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) : mScene(pScene) { list attr_list; diff --git a/code/X3DImporter_Metadata.cpp b/code/X3DImporter_Metadata.cpp index 9d7147aea..0fa1b27b3 100644 --- a/code/X3DImporter_Metadata.cpp +++ b/code/X3DImporter_Metadata.cpp @@ -105,7 +105,7 @@ bool X3DImporter::ParseHelper_CheckRead_X3DMetadataObject() return true; } -void X3DImporter::ParseNode_Metadata(CX3DImporter_NodeElement* pParentElement, const std::string& pNodeName) +void X3DImporter::ParseNode_Metadata(CX3DImporter_NodeElement* pParentElement, const std::string& /*pNodeName*/) { ParseHelper_Node_Enter(pParentElement); MACRO_NODECHECK_METADATA(mReader->getNodeName()); diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index a98fe5ab2..e252eb871 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -403,7 +403,7 @@ namespace glTF2 virtual ~Object() {} //! Maps special IDs to another ID, where needed. Subclasses may override it (statically) - static const char* TranslateId(Asset& r, const char* id) + static const char* TranslateId(Asset& /*r*/, const char* id) { return id; } }; diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 8b50fa1d3..041e73223 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -288,7 +288,7 @@ inline Buffer::~Buffer() for(SEncodedRegion* reg : EncodedRegion_List) delete reg; } -inline const char* Buffer::TranslateId(Asset& r, const char* id) +inline const char* Buffer::TranslateId(Asset& /*r*/, const char* id) { return id; } @@ -623,7 +623,7 @@ inline Image::Image() } -inline void Image::Read(Value& obj, Asset& r) +inline void Image::Read(Value& obj, Asset& /*r*/) { if (!mDataLength) { if (Value* uri = FindString(obj, "uri")) { @@ -668,7 +668,7 @@ inline void Image::SetData(uint8_t* data, size_t length, Asset& r) } } -inline void Sampler::Read(Value& obj, Asset& r) +inline void Sampler::Read(Value& obj, Asset& /*r*/) { SetDefaults(); @@ -886,7 +886,7 @@ inline void Mesh::Read(Value& pJSON_Object, Asset& pAsset_Root) } } -inline void Camera::Read(Value& obj, Asset& r) +inline void Camera::Read(Value& obj, Asset& /*r*/) { type = MemberOrDefault(obj, "type", Camera::Perspective); @@ -1087,7 +1087,7 @@ inline void Asset::ReadExtensionsUsed(Document& doc) #undef CHECK_EXT } -inline IOStream* Asset::OpenFile(std::string path, const char* mode, bool absolute) +inline IOStream* Asset::OpenFile(std::string path, const char* mode, bool /*absolute*/) { #ifdef ASSIMP_API return mIOSystem->Open(path, mode); diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index df28cb681..8583462ce 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -72,7 +72,7 @@ namespace glTF2 { return val; } - inline Value& MakeValue(Value& val, float r, MemoryPoolAllocator<>& al) { + inline Value& MakeValue(Value& val, float r, MemoryPoolAllocator<>& /*al*/) { val.SetDouble(r); return val; @@ -171,7 +171,7 @@ namespace glTF2 { obj.AddMember("target", int(bv.target), w.mAl); } - inline void Write(Value& obj, Camera& c, AssetWriter& w) + inline void Write(Value& /*obj*/, Camera& /*c*/, AssetWriter& /*w*/) { } @@ -432,7 +432,7 @@ namespace glTF2 { } } - inline void Write(Value& obj, Program& b, AssetWriter& w) + inline void Write(Value& /*obj*/, Program& /*b*/, AssetWriter& /*w*/) { } @@ -465,7 +465,7 @@ namespace glTF2 { AddRefsVector(scene, "nodes", s.nodes, w.mAl); } - inline void Write(Value& obj, Shader& b, AssetWriter& w) + inline void Write(Value& /*obj*/, Shader& /*b*/, AssetWriter& /*w*/) { } diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 9e6415522..da88dc65f 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -80,7 +80,7 @@ namespace Assimp { } // end of namespace Assimp glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const aiScene* pScene, - const ExportProperties* pProperties, bool isBinary) + const ExportProperties* pProperties, bool /*isBinary*/) : mFilename(filename) , mIOSystem(pIOSystem) , mProperties(pProperties) diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 292e0eeb2..5d40d75bc 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -159,21 +159,21 @@ static void CopyValue(const glTF2::mat4& v, aiMatrix4x4& o) o.a4 = v[12]; o.b4 = v[13]; o.c4 = v[14]; o.d4 = v[15]; } -inline void SetMaterialColorProperty(Asset& r, vec4& prop, aiMaterial* mat, const char* pKey, unsigned int type, unsigned int idx) +inline void SetMaterialColorProperty(Asset& /*r*/, vec4& prop, aiMaterial* mat, const char* pKey, unsigned int type, unsigned int idx) { aiColor4D col; CopyValue(prop, col); mat->AddProperty(&col, 1, pKey, type, idx); } -inline void SetMaterialColorProperty(Asset& r, vec3& prop, aiMaterial* mat, const char* pKey, unsigned int type, unsigned int idx) +inline void SetMaterialColorProperty(Asset& /*r*/, vec3& prop, aiMaterial* mat, const char* pKey, unsigned int type, unsigned int idx) { aiColor4D col; CopyValue(prop, col); mat->AddProperty(&col, 1, pKey, type, idx); } -inline void SetMaterialTextureProperty(std::vector& embeddedTexIdxs, Asset& r, glTF2::TextureInfo prop, aiMaterial* mat, aiTextureType texType, unsigned int texSlot = 0) +inline void SetMaterialTextureProperty(std::vector& embeddedTexIdxs, Asset& /*r*/, glTF2::TextureInfo prop, aiMaterial* mat, aiTextureType texType, unsigned int texSlot = 0) { if (prop.texture && prop.texture->source) { aiString uri(prop.texture->source->uri); @@ -296,6 +296,7 @@ static inline void SetFace(aiFace& face, int a, int b, int c) face.mIndices[2] = c; } +#ifdef ASSIMP_BUILD_DEBUG static inline bool CheckValidFacesIndices(aiFace* faces, unsigned nFaces, unsigned nVerts) { for (unsigned i = 0; i < nFaces; ++i) { @@ -307,6 +308,7 @@ static inline bool CheckValidFacesIndices(aiFace* faces, unsigned nFaces, unsign } return true; } +#endif // ASSIMP_BUILD_DEBUG void glTF2Importer::ImportMeshes(glTF2::Asset& r) { diff --git a/code/glTFAsset.h b/code/glTFAsset.h index e018e5ace..0c3de16a5 100644 --- a/code/glTFAsset.h +++ b/code/glTFAsset.h @@ -393,7 +393,7 @@ namespace glTF virtual ~Object() {} //! Maps special IDs to another ID, where needed. Subclasses may override it (statically) - static const char* TranslateId(Asset& r, const char* id) + static const char* TranslateId(Asset& /*r*/, const char* id) { return id; } }; diff --git a/code/glTFAsset.inl b/code/glTFAsset.inl index 61afebfb4..6d328614e 100644 --- a/code/glTFAsset.inl +++ b/code/glTFAsset.inl @@ -675,7 +675,7 @@ inline void Image::SetData(uint8_t* data, size_t length, Asset& r) } } -inline void Sampler::Read(Value& obj, Asset& r) +inline void Sampler::Read(Value& obj, Asset& /*r*/) { SetDefaults(); @@ -1093,7 +1093,7 @@ Ref buf = pAsset_Root.buffers.Get(pCompression_Open3DGC.Buffer); } #endif -inline void Camera::Read(Value& obj, Asset& r) +inline void Camera::Read(Value& obj, Asset& /*r*/) { type = MemberOrDefault(obj, "type", Camera::Perspective); @@ -1116,7 +1116,7 @@ inline void Camera::Read(Value& obj, Asset& r) } } -inline void Light::Read(Value& obj, Asset& r) +inline void Light::Read(Value& obj, Asset& /*r*/) { SetDefaults(); @@ -1414,7 +1414,7 @@ inline void Asset::ReadExtensionsUsed(Document& doc) #undef CHECK_EXT } -inline IOStream* Asset::OpenFile(std::string path, const char* mode, bool absolute) +inline IOStream* Asset::OpenFile(std::string path, const char* mode, bool /*absolute*/) { #ifdef ASSIMP_API return mIOSystem->Open(path, mode); diff --git a/code/glTFAssetWriter.inl b/code/glTFAssetWriter.inl index 698f0ba8f..485abc4f6 100644 --- a/code/glTFAssetWriter.inl +++ b/code/glTFAssetWriter.inl @@ -187,7 +187,7 @@ namespace glTF { obj.AddMember("target", int(bv.target), w.mAl); } - inline void Write(Value& obj, Camera& c, AssetWriter& w) + inline void Write(Value& /*obj*/, Camera& /*c*/, AssetWriter& /*w*/) { } @@ -398,7 +398,7 @@ namespace glTF { } } - inline void Write(Value& obj, Program& b, AssetWriter& w) + inline void Write(Value& /*obj*/, Program& /*b*/, AssetWriter& /*w*/) { } @@ -424,7 +424,7 @@ namespace glTF { AddRefsVector(scene, "nodes", s.nodes, w.mAl); } - inline void Write(Value& obj, Shader& b, AssetWriter& w) + inline void Write(Value& /*obj*/, Shader& /*b*/, AssetWriter& /*w*/) { } @@ -452,7 +452,7 @@ namespace glTF { } - inline void Write(Value& obj, Technique& b, AssetWriter& w) + inline void Write(Value& /*obj*/, Technique& /*b*/, AssetWriter& /*w*/) { } @@ -467,7 +467,7 @@ namespace glTF { } } - inline void Write(Value& obj, Light& b, AssetWriter& w) + inline void Write(Value& /*obj*/, Light& /*b*/, AssetWriter& /*w*/) { } diff --git a/code/glTFImporter.cpp b/code/glTFImporter.cpp index a1d8e0cd0..5d1b5afab 100644 --- a/code/glTFImporter.cpp +++ b/code/glTFImporter.cpp @@ -154,7 +154,7 @@ static void CopyValue(const glTF::mat4& v, aiMatrix4x4& o) o.a4 = v[12]; o.b4 = v[13]; o.c4 = v[14]; o.d4 = v[15]; } -inline void SetMaterialColorProperty(std::vector& embeddedTexIdxs, Asset& r, glTF::TexProperty prop, aiMaterial* mat, +inline void SetMaterialColorProperty(std::vector& embeddedTexIdxs, Asset& /*r*/, glTF::TexProperty prop, aiMaterial* mat, aiTextureType texType, const char* pKey, unsigned int type, unsigned int idx) { if (prop.texture) { @@ -234,6 +234,7 @@ static inline void SetFace(aiFace& face, int a, int b, int c) face.mIndices[2] = c; } +#ifdef ASSIMP_BUILD_DEBUG static inline bool CheckValidFacesIndices(aiFace* faces, unsigned nFaces, unsigned nVerts) { for (unsigned i = 0; i < nFaces; ++i) { @@ -245,6 +246,7 @@ static inline bool CheckValidFacesIndices(aiFace* faces, unsigned nFaces, unsign } return true; } +#endif // ASSIMP_BUILD_DEBUG void glTFImporter::ImportMeshes(glTF::Asset& r) { diff --git a/contrib/Open3DGC/o3dgcTriangleListEncoder.inl b/contrib/Open3DGC/o3dgcTriangleListEncoder.inl index d499f6dc9..017dd86bf 100644 --- a/contrib/Open3DGC/o3dgcTriangleListEncoder.inl +++ b/contrib/Open3DGC/o3dgcTriangleListEncoder.inl @@ -125,7 +125,7 @@ namespace o3dgc } return true; } - inline bool IsCase6(long degree, long numIndices, const long * const ops, const long * const indices) + inline bool IsCase6(long degree, long numIndices, const long * const ops, const long * const /*indices*/) { // ops: 0000000 indices: if (numIndices!= 0) diff --git a/contrib/openddlparser/code/DDLNode.cpp b/contrib/openddlparser/code/DDLNode.cpp index 6548cdf24..a7c557fc5 100644 --- a/contrib/openddlparser/code/DDLNode.cpp +++ b/contrib/openddlparser/code/DDLNode.cpp @@ -191,7 +191,7 @@ Reference *DDLNode::getReferences() const { return m_references; } -void DDLNode::dump(IOStreamBase &stream) { +void DDLNode::dump(IOStreamBase &/*stream*/) { // Todo! } diff --git a/contrib/openddlparser/code/Value.cpp b/contrib/openddlparser/code/Value.cpp index 3c8b06a10..b5a35e722 100644 --- a/contrib/openddlparser/code/Value.cpp +++ b/contrib/openddlparser/code/Value.cpp @@ -294,7 +294,7 @@ Reference *Value::getRef() const { return (Reference*) m_data; } -void Value::dump( IOStreamBase &stream ) { +void Value::dump( IOStreamBase &/*stream*/ ) { switch( m_type ) { case ddl_none: std::cout << "None" << std::endl; diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLParser.h b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h index e9ac665b5..cdf6f7288 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLParser.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h @@ -41,7 +41,7 @@ struct Property; template inline -bool isEmbeddedCommentOpenTag( T *in, T *end ) { +bool isEmbeddedCommentOpenTag( T *in, T */*end*/ ) { if ( in == '/' && in+1 == '*' ) { return true; } diff --git a/include/assimp/Exporter.hpp b/include/assimp/Exporter.hpp index 2c141ded2..c6a6f684a 100644 --- a/include/assimp/Exporter.hpp +++ b/include/assimp/Exporter.hpp @@ -171,7 +171,7 @@ public: * Any IO handlers set via #SetIOHandler are ignored here. * @note Use aiCopyScene() to get a modifiable copy of a previously * imported scene. */ - const aiExportDataBlob* ExportToBlob( const aiScene* pScene, const char* pFormatId, unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = NULL); + const aiExportDataBlob* ExportToBlob(const aiScene* pScene, const char* pFormatId, unsigned int pPreprocessing = 0u, const ExportProperties* = NULL); const aiExportDataBlob* ExportToBlob( const aiScene* pScene, const std::string& pFormatId, unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = NULL); // ------------------------------------------------------------------- From c86c7b451829c53225da9694c0421aba2e66a04d Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 3 Nov 2017 13:11:30 +0100 Subject: [PATCH 273/490] Update .travis.sh Retrigger travis. --- .travis.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.sh b/.travis.sh index 24aff9368..75f00ec60 100755 --- a/.travis.sh +++ b/.travis.sh @@ -5,8 +5,7 @@ # # License see LICENSE file # -function generate() -{ +function generate() { OPTIONS="-DASSIMP_WERROR=ON" if [ "$DISABLE_EXPORTERS" = "YES" ] ; then From 2922753589567c066d0a9721b14d04820c688566 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Fri, 3 Nov 2017 11:50:28 -0400 Subject: [PATCH 274/490] Return early when element is TextureFile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In cases where the TextureFile name would start with an integer, `strtoul10` would parse that number and incorrectly set `numOccur` to that number. This caused PLY parsing to fail for vertex positions. Since TextureFile is a single line, and does not have any follow-up lines, it’s okay to return early --- code/PlyParser.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/code/PlyParser.cpp b/code/PlyParser.cpp index f4b68a83f..65de665ab 100644 --- a/code/PlyParser.cpp +++ b/code/PlyParser.cpp @@ -381,6 +381,11 @@ bool PLY::Element::ParseElement(IOStreamBuffer &streamBuffer, std::vector< { char* endPos = &buffer[0] + (strlen(&buffer[0]) - 1); pOut->szName = std::string(&buffer[0], endPos); + + // go to the next line + PLY::DOM::SkipSpacesAndLineEnd(buffer); + + return true; } //parse the number of occurrences of this element @@ -933,7 +938,7 @@ bool PLY::PropertyInstance::ParseValue(const char* &pCur, { ai_assert(NULL != pCur); ai_assert(NULL != out); - + //calc element size bool ret = true; switch (eType) From 9a721d0ef4759b7caa4f24254ab4c990788ef6af Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 4 Nov 2017 08:24:36 +0100 Subject: [PATCH 275/490] Update .travis.yml Remove unused branch rule for travis. --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index fa37e5955..95e2fc164 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,10 +18,6 @@ before_install: # install latest LCOV (1.9 was failing) - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ${TRAVIS_BUILD_DIR} && wget http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.11.orig.tar.gz && tar xf lcov_1.11.orig.tar.gz && sudo make -C lcov-1.11/ install && gem install coveralls-lcov && lcov --version && g++ --version ; fi -branches: - only: - - master - os: - linux From c9ada44ab54c63627ba9fa0a50dbb4a16dc827fc Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 4 Nov 2017 17:05:23 +0100 Subject: [PATCH 276/490] Fix memory leak in case of an error. --- code/ObjFileParser.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/ObjFileParser.cpp b/code/ObjFileParser.cpp index 4b203a8c2..0435f9b88 100644 --- a/code/ObjFileParser.cpp +++ b/code/ObjFileParser.cpp @@ -477,6 +477,8 @@ void ObjFileParser::getFace( aiPrimitiveType type ) { } } else { //On error, std::atoi will return 0 which is not a valid value + delete m_pModel; + m_pModel = nullptr; throw DeadlyImportError("OBJ: Invalid face indice"); } From a33e115fd1dacc9251fc5ae6fde2a94e11b678dd Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 4 Nov 2017 18:26:30 +0100 Subject: [PATCH 277/490] fix mem-lead: face will be not released in case of an error. --- code/ObjFileParser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/ObjFileParser.cpp b/code/ObjFileParser.cpp index 0435f9b88..d89d52977 100644 --- a/code/ObjFileParser.cpp +++ b/code/ObjFileParser.cpp @@ -477,6 +477,7 @@ void ObjFileParser::getFace( aiPrimitiveType type ) { } } else { //On error, std::atoi will return 0 which is not a valid value + delete face; delete m_pModel; m_pModel = nullptr; throw DeadlyImportError("OBJ: Invalid face indice"); From 02b042d78eea03841d288b1cf08f333c1632f88e Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 5 Nov 2017 16:35:22 +0100 Subject: [PATCH 278/490] closes https://github.com/assimp/assimp/issues/1351: use correct name for obj-meshname export for groups. --- code/ObjExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ObjExporter.cpp b/code/ObjExporter.cpp index 3f9fabdc4..fffafa328 100644 --- a/code/ObjExporter.cpp +++ b/code/ObjExporter.cpp @@ -383,7 +383,7 @@ void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4 mMeshes.push_back(MeshInstance()); MeshInstance& mesh = mMeshes.back(); - mesh.name = std::string(name.data,name.length) + (m->mName.length ? "_" + std::string(m->mName.data,m->mName.length) : ""); + mesh.name = std::string( name.data, name.length ); mesh.matname = GetMaterialName(m->mMaterialIndex); mesh.faces.resize(m->mNumFaces); From 2929a27edcefc48bb8ab35b3ded75b81ec676894 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 5 Nov 2017 17:41:26 +0100 Subject: [PATCH 279/490] add unittest for x3d-importer. --- code/OpenGEXImporter.cpp | 22 +++++++++---------- .../include/openddlparser/OpenDDLParser.h | 6 ++++- test/CMakeLists.txt | 1 + 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 27d7b3e49..52e9ce501 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -431,7 +431,7 @@ void OpenGEXImporter::handleNodes( DDLNode *node, aiScene *pScene ) { } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleMetricNode( DDLNode *node, aiScene */*pScene*/ ) { +void OpenGEXImporter::handleMetricNode( DDLNode *node, aiScene * /*pScene*/ ) { if( nullptr == node || nullptr == m_ctx ) { return; } @@ -467,7 +467,7 @@ void OpenGEXImporter::handleMetricNode( DDLNode *node, aiScene */*pScene*/ ) { } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleNameNode( DDLNode *node, aiScene */*pScene*/ ) { +void OpenGEXImporter::handleNameNode( DDLNode *node, aiScene * /*pScene*/ ) { if( nullptr == m_currentNode ) { throw DeadlyImportError( "No current node for name." ); return; @@ -512,7 +512,7 @@ static void getRefNames( DDLNode *node, std::vector &names ) { } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleObjectRefNode( DDLNode *node, aiScene */*pScene*/ ) { +void OpenGEXImporter::handleObjectRefNode( DDLNode *node, aiScene * /*pScene*/ ) { if( nullptr == m_currentNode ) { throw DeadlyImportError( "No parent node for name." ); return; @@ -536,7 +536,7 @@ void OpenGEXImporter::handleObjectRefNode( DDLNode *node, aiScene */*pScene*/ ) } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleMaterialRefNode( ODDLParser::DDLNode *node, aiScene */*pScene*/ ) { +void OpenGEXImporter::handleMaterialRefNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) { if( nullptr == m_currentNode ) { throw DeadlyImportError( "No parent node for name." ); return; @@ -674,7 +674,7 @@ static void setMatrix( aiNode *node, DataArrayList *transformData ) { } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleTransformNode( ODDLParser::DDLNode *node, aiScene */*pScene*/ ) { +void OpenGEXImporter::handleTransformNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) { if( nullptr == m_currentNode ) { throw DeadlyImportError( "No parent node for name." ); return; @@ -819,7 +819,7 @@ static void copyColor4DArray( size_t numItems, DataArrayList *vaList, aiColor4D } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleVertexArrayNode( ODDLParser::DDLNode *node, aiScene */*pScene*/ ) { +void OpenGEXImporter::handleVertexArrayNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) { if( nullptr == node ) { throw DeadlyImportError( "No parent node for name." ); return; @@ -862,7 +862,7 @@ void OpenGEXImporter::handleVertexArrayNode( ODDLParser::DDLNode *node, aiScene } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleIndexArrayNode( ODDLParser::DDLNode *node, aiScene */*pScene*/ ) { +void OpenGEXImporter::handleIndexArrayNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) { if( nullptr == node ) { throw DeadlyImportError( "No parent node for name." ); return; @@ -1001,7 +1001,7 @@ void OpenGEXImporter::handleMaterialNode( ODDLParser::DDLNode *node, aiScene *pS } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleColorNode( ODDLParser::DDLNode *node, aiScene */*pScene*/ ) { +void OpenGEXImporter::handleColorNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) { if( nullptr == node ) { return; } @@ -1040,7 +1040,7 @@ void OpenGEXImporter::handleColorNode( ODDLParser::DDLNode *node, aiScene */*pSc } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleTextureNode( ODDLParser::DDLNode *node, aiScene */*pScene*/ ) { +void OpenGEXImporter::handleTextureNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) { if( nullptr == node ) { return; } @@ -1074,7 +1074,7 @@ void OpenGEXImporter::handleTextureNode( ODDLParser::DDLNode *node, aiScene */*p } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleParamNode( ODDLParser::DDLNode *node, aiScene */*pScene*/ ) { +void OpenGEXImporter::handleParamNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) { if ( nullptr == node ) { return; } @@ -1103,7 +1103,7 @@ void OpenGEXImporter::handleParamNode( ODDLParser::DDLNode *node, aiScene */*pSc } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleAttenNode( ODDLParser::DDLNode *node, aiScene */*pScene*/ ) { +void OpenGEXImporter::handleAttenNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) { if ( nullptr == node ) { return; } diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLParser.h b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h index cdf6f7288..ef7f3a72e 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLParser.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h @@ -41,7 +41,11 @@ struct Property; template inline -bool isEmbeddedCommentOpenTag( T *in, T */*end*/ ) { +bool isEmbeddedCommentOpenTag( T *in, T *end ) { + if ( in == end ) { + return false; + } + if ( in == '/' && in+1 == '*' ) { return true; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 62270b935..634ce5e15 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -129,6 +129,7 @@ SET( TEST_SRCS unit/utVersion.cpp unit/utVector3.cpp unit/utXImporterExporter.cpp + unit/utX3DImportExport.cpp unit/utD3MFImportExport.cpp unit/utQ3DImportExport.cpp unit/utProfiler.cpp From 770f531cc6042becc7fbc6d7cac1987f9ed292f4 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 5 Nov 2017 17:46:25 +0100 Subject: [PATCH 280/490] X3D-Importer: add missing file. --- test/unit/utX3DImportExport.cpp | 62 +++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 test/unit/utX3DImportExport.cpp diff --git a/test/unit/utX3DImportExport.cpp b/test/unit/utX3DImportExport.cpp new file mode 100644 index 000000000..a84c58efc --- /dev/null +++ b/test/unit/utX3DImportExport.cpp @@ -0,0 +1,62 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +#include "UnitTestPCH.h" +#include "SceneDiffer.h" +#include "AbstractImportExportBase.h" + +#include + +using namespace Assimp; + +class utX3DImportExport : public AbstractImportExportBase { +public: + virtual bool importerTest() { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/X3D/ComputerKeyboard.x3d", 0 ); + return nullptr != scene; + } +}; + +TEST_F( utX3DImportExport, importX3DFromFileTest ) { + EXPECT_TRUE( importerTest() ); +} From f43586305ce3e18708743a42b23f73003fbbf378 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 5 Nov 2017 18:24:39 +0100 Subject: [PATCH 281/490] closes https://github.com/assimp/assimp/issues/1533: put irrXML onto exclucde list for doxygen run. --- doc/Doxyfile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index ebb6b72b8..cb629a985 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -725,7 +725,7 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = +EXCLUDE = irrXML.h # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded From 6b04b208697c83a27d74e2d0ee6e7e003d429a8b Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 5 Nov 2017 22:41:41 +0100 Subject: [PATCH 282/490] closes https://github.com/assimp/assimp/issues/1526: use correct include folder for assimp. --- assimp.pc.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assimp.pc.in b/assimp.pc.in index 5b2139e6d..02cf59dc4 100644 --- a/assimp.pc.in +++ b/assimp.pc.in @@ -1,7 +1,7 @@ prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_PREFIX@/ libdir=@CMAKE_INSTALL_PREFIX@/@ASSIMP_LIB_INSTALL_DIR@ -includedir=@CMAKE_INSTALL_PREFIX@/@ASSIMP_INCLUDE_INSTALL_DIR@/assimp +includedir=@CMAKE_INSTALL_PREFIX@/@ASSIMP_INCLUDE_INSTALL_DIR@ Name: @CMAKE_PROJECT_NAME@ Description: Import various well-known 3D model formats in an uniform manner. From 4a4f3fddc7b1b6619e870502f3cd255148900b94 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 5 Nov 2017 22:52:07 +0100 Subject: [PATCH 283/490] closes https://github.com/assimp/assimp/issues/213: log an error instead of letting the fbx-importer crash. --- code/FBXConverter.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index a1121cc40..a1e3ae07c 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -645,7 +645,6 @@ void Converter::ConvertCameras( const Model& model ) } } - void Converter::ConvertLight( const Model& model, const Light& light ) { lights.push_back( new aiLight() ); @@ -783,7 +782,6 @@ const char* Converter::NameTransformationComp( TransformationComp comp ) return NULL; } - const char* Converter::NameTransformationCompProperty( TransformationComp comp ) { switch ( comp ) @@ -2239,9 +2237,17 @@ void Converter::ConvertAnimations() } } +void Converter::RenameNode( const std::string& fixed_name, const std::string& new_name ) { + if ( node_names.find( fixed_name ) == node_names.end() ) { + FBXImporter::LogError( "Cannot rename node " + fixed_name + ", not existing."); + return; + } + + if ( node_names.find( new_name ) != node_names.end() ) { + FBXImporter::LogError( "Cannot rename node " + fixed_name + " to " + new_name +", name already existing." ); + return; + } -void Converter::RenameNode( const std::string& fixed_name, const std::string& new_name ) -{ ai_assert( node_names.find( fixed_name ) != node_names.end() ); ai_assert( node_names.find( new_name ) == node_names.end() ); From 9a9f18bbed8938323548335d9225e480a0ea8ca6 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 6 Nov 2017 22:30:07 +0100 Subject: [PATCH 284/490] closes https://github.com/assimp/assimp/issues/104: deal with more solids in one STL file. --- code/STLLoader.cpp | 86 ++++++++++++++++++++++++++++------------------ code/STLLoader.h | 60 ++++++++++++++++++-------------- 2 files changed, 86 insertions(+), 60 deletions(-) diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index f4d6ddda7..6b71ba920 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -200,17 +200,17 @@ void STLImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS if (IsBinarySTL(mBuffer, fileSize)) { bMatClr = LoadBinaryFile(); } else if (IsAsciiSTL(mBuffer, fileSize)) { - LoadASCIIFile(); + LoadASCIIFile( pScene->mRootNode ); } else { throw DeadlyImportError( "Failed to determine STL storage representation for " + pFile + "."); } // add all created meshes to the single node - pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; + /*pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; for (unsigned int i = 0; i < pScene->mNumMeshes; i++) pScene->mRootNode->mMeshes[i] = i; - + */ // create a single default material, using a white diffuse color for consistency with // other geometric types (e.g., PLY). aiMaterial* pcMat = new aiMaterial(); @@ -231,11 +231,12 @@ void STLImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS pScene->mMaterials = new aiMaterial*[1]; pScene->mMaterials[0] = pcMat; } + // ------------------------------------------------------------------------------------------------ // Read an ASCII STL file -void STLImporter::LoadASCIIFile() -{ +void STLImporter::LoadASCIIFile( aiNode *root ) { std::vector meshes; + std::vector nodes; const char* sz = mBuffer; const char* bufferEnd = mBuffer + fileSize; std::vector positionBuffer; @@ -247,12 +248,15 @@ void STLImporter::LoadASCIIFile() positionBuffer.reserve(sizeEstimate); normalBuffer.reserve(sizeEstimate); - while (IsAsciiSTL(sz, static_cast(bufferEnd - sz))) - { + while (IsAsciiSTL(sz, static_cast(bufferEnd - sz))) { + std::vector meshIndices; aiMesh* pMesh = new aiMesh(); pMesh->mMaterialIndex = 0; + meshIndices.push_back( meshes.size() ); meshes.push_back(pMesh); - + aiNode *node = new aiNode; + node->mParent = root; + nodes.push_back( node ); SkipSpaces(&sz); ai_assert(!IsLineEnd(sz)); @@ -265,20 +269,21 @@ void STLImporter::LoadASCIIFile() size_t temp; // setup the name of the node - if ((temp = (size_t)(sz-szMe))) { + if ((temp = (size_t)(sz-szMe))) { if (temp >= MAXLEN) { throw DeadlyImportError( "STL: Node name too long" ); } - - pScene->mRootNode->mName.length = temp; - memcpy(pScene->mRootNode->mName.data,szMe,temp); - pScene->mRootNode->mName.data[temp] = '\0'; + std::string name( szMe, temp ); + node->mName.Set( name.c_str() ); + //pScene->mRootNode->mName.length = temp; + //memcpy(pScene->mRootNode->mName.data,szMe,temp); + //pScene->mRootNode->mName.data[temp] = '\0'; + } else { + pScene->mRootNode->mName.Set(""); } - else pScene->mRootNode->mName.Set(""); unsigned int faceVertexCounter = 3; - for ( ;; ) - { + for ( ;; ) { // go to the next token if(!SkipSpacesAndLineEnd(&sz)) { @@ -300,9 +305,7 @@ void STLImporter::LoadASCIIFile() SkipSpaces(&sz); if (strncmp(sz,"normal",6)) { DefaultLogger::get()->warn("STL: a facet normal vector was expected but not found"); - } - else - { + } else { if (sz[6] == '\0') { throw DeadlyImportError("STL: unexpected EOF while parsing facet"); } @@ -316,16 +319,11 @@ void STLImporter::LoadASCIIFile() normalBuffer.push_back(*vn); normalBuffer.push_back(*vn); } - } - // vertex 1.50000 1.50000 0.00000 - else if (!strncmp(sz,"vertex",6) && ::IsSpaceOrNewLine(*(sz+6))) - { + } else if (!strncmp(sz,"vertex",6) && ::IsSpaceOrNewLine(*(sz+6))) { // vertex 1.50000 1.50000 0.00000 if (faceVertexCounter >= 3) { DefaultLogger::get()->error("STL: a facet with more than 3 vertices has been found"); ++sz; - } - else - { + } else { if (sz[6] == '\0') { throw DeadlyImportError("STL: unexpected EOF while parsing facet"); } @@ -340,17 +338,14 @@ void STLImporter::LoadASCIIFile() sz = fast_atoreal_move(sz, (ai_real&)vn->z ); faceVertexCounter++; } - } - else if (!::strncmp(sz,"endsolid",8)) { + } else if (!::strncmp(sz,"endsolid",8)) { do { ++sz; } while (!::IsLineEnd(*sz)); SkipSpacesAndLineEnd(&sz); // finished! break; - } - // else skip the whole identifier - else { + } else { // else skip the whole identifier do { ++sz; } while (!::IsSpaceOrNewLine(*sz)); @@ -380,13 +375,22 @@ void STLImporter::LoadASCIIFile() // now copy faces addFacesToMesh(pMesh); + + // assign the meshes to the current node + pushMeshesToNode( meshIndices, node ); } + // now add the loaded meshes pScene->mNumMeshes = (unsigned int)meshes.size(); pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; - for (size_t i = 0; i < meshes.size(); i++) - { - pScene->mMeshes[i] = meshes[i]; + for (size_t i = 0; i < meshes.size(); i++) { + pScene->mMeshes[ i ] = meshes[i]; + } + + root->mNumChildren = nodes.size(); + root->mChildren = new aiNode*[ root->mNumChildren ]; + for ( size_t i=0; imChildren[ i ] = nodes[ i ]; } } @@ -513,4 +517,18 @@ bool STLImporter::LoadBinaryFile() return false; } +void STLImporter::pushMeshesToNode( std::vector &meshIndices, aiNode *node ) { + ai_assert( nullptr != node ); + if ( meshIndices.empty() ) { + return; + } + + node->mNumMeshes = static_cast( meshIndices.size() ); + node->mMeshes = new unsigned int[ meshIndices.size() ]; + for ( size_t i=0; imMeshes[ i ] = meshIndices[ i ]; + } + meshIndices.clear(); +} + #endif // !! ASSIMP_BUILD_NO_STL_IMPORTER diff --git a/code/STLLoader.h b/code/STLLoader.h index 87ed3288d..ff7b32a15 100644 --- a/code/STLLoader.h +++ b/code/STLLoader.h @@ -48,53 +48,61 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "BaseImporter.h" #include -namespace Assimp { +// Forward declarations +class aiNode; + +namespace Assimp { + // --------------------------------------------------------------------------- -/** Importer class for the sterolithography STL file format -*/ -class STLImporter : public BaseImporter -{ +/** + * @brief Importer class for the sterolithography STL file format. + */ +class STLImporter : public BaseImporter { public: + /** + * @brief STLImporter, the class default constructor. + */ STLImporter(); + + /** + * @brief The class destructor. + */ ~STLImporter(); - -public: - - // ------------------------------------------------------------------- - /** Returns whether the class can handle the format of the given file. - * See BaseImporter::CanRead() for details. + /** + * @brief Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. */ - bool CanRead( const std::string& pFile, IOSystem* pIOHandler, - bool checkSig) const; + bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; protected: - // ------------------------------------------------------------------- - /** Return importer meta information. - * See #BaseImporter::GetInfo for the details + /** + * @brief Return importer meta information. + * See #BaseImporter::GetInfo for the details */ const aiImporterDesc* GetInfo () const; - // ------------------------------------------------------------------- - /** Imports the given file into the given scene structure. + /** + * @brief Imports the given file into the given scene structure. * See BaseImporter::InternReadFile() for details */ void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); - - // ------------------------------------------------------------------- - /** Loads a binary .stl file + /** + * @brief Loads a binary .stl file * @return true if the default vertex color must be used as material color - */ + */ bool LoadBinaryFile(); - // ------------------------------------------------------------------- - /** Loads a ASCII text .stl file - */ - void LoadASCIIFile(); + /** + * @brief Loads a ASCII text .stl file + */ + void LoadASCIIFile( aiNode *root ); + + void pushMeshesToNode( std::vector &meshIndices, aiNode *node ); protected: From 26171a7949178d6b98140c749ccc9806792b4fa6 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 6 Nov 2017 22:37:52 +0100 Subject: [PATCH 285/490] SLD: add test model and a unit test. --- test/CMakeLists.txt | 1 + test/models/STL/triangle_with_two_solids.stl | 18 ++++++ test/unit/utSTLImportExport.cpp | 68 ++++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 test/models/STL/triangle_with_two_solids.stl create mode 100644 test/unit/utSTLImportExport.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 62270b935..e50e8a742 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -131,6 +131,7 @@ SET( TEST_SRCS unit/utXImporterExporter.cpp unit/utD3MFImportExport.cpp unit/utQ3DImportExport.cpp + unit/utSTLImportExport.cpp unit/utProfiler.cpp ) SET( POST_PROCESSES diff --git a/test/models/STL/triangle_with_two_solids.stl b/test/models/STL/triangle_with_two_solids.stl new file mode 100644 index 000000000..9af3841ae --- /dev/null +++ b/test/models/STL/triangle_with_two_solids.stl @@ -0,0 +1,18 @@ +solid testTriangle_1 + facet normal 0.0 0.0 1.0 + outer loop + vertex 1.0 1.0 0.0 + vertex -1.0 1.0 0.0 + vertex 0.0 -1.0 0.0 + endloop + endfacet +endsolid +solid testTriangle_2 + facet normal 0.0 0.0 1.0 + outer loop + vertex 3.0 3.0 0.0 + vertex 2.0 3.0 0.0 + vertex 0.0 2.0 0.0 + endloop + endfacet +endsolid diff --git a/test/unit/utSTLImportExport.cpp b/test/unit/utSTLImportExport.cpp new file mode 100644 index 000000000..0ee3de955 --- /dev/null +++ b/test/unit/utSTLImportExport.cpp @@ -0,0 +1,68 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +#include "UnitTestPCH.h" +#include "SceneDiffer.h" +#include "AbstractImportExportBase.h" + +#include + +using namespace Assimp; + +class utSTLImporterExporter : public AbstractImportExportBase { +public: + virtual bool importerTest() { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/STL/Spider_ascii.stl", 0 ); + return nullptr != scene; + } +}; + +TEST_F( utSTLImporterExporter, importXFromFileTest ) { + EXPECT_TRUE( importerTest() ); +} + +TEST_F( utSTLImporterExporter, test_with_two_solids ) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/STL/triangle_with_two_solids.stl", 0 ); + EXPECT_NE( nullptr, scene ); +} From 4ff2592747787eedca14e24ad1d088250eab86da Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 7 Nov 2017 00:31:09 +0100 Subject: [PATCH 286/490] Update STLLoader.h Fixed a typo. --- code/STLLoader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/STLLoader.h b/code/STLLoader.h index ff7b32a15..c51604861 100644 --- a/code/STLLoader.h +++ b/code/STLLoader.h @@ -49,7 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include // Forward declarations -class aiNode; +struct aiNode; namespace Assimp { From b87e7643d2534a561301631bf87cdee3397bf366 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 7 Nov 2017 10:42:51 +0100 Subject: [PATCH 287/490] Update STLLoader.cpp Fix memory-alignment bug. --- code/STLLoader.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index 6b71ba920..a492d47e8 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -80,7 +80,9 @@ static bool IsBinarySTL(const char* buffer, unsigned int fileSize) { return false; } - const uint32_t faceCount = *reinterpret_cast(buffer + 80); + char *facecount_pos = buffer + 80; + uint32_t faceCount( 0 ); + ::memcpy( &faceCount, facecount_pos, sizeof( uint32_t ) ); const uint32_t expectedBinaryFileSize = faceCount * 50 + 84; return expectedBinaryFileSize == fileSize; From da7ce89ff23425b217194c3d1bde06d4d2a5936b Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 7 Nov 2017 10:47:27 +0100 Subject: [PATCH 288/490] Update STLLoader.cpp add missing const. --- code/STLLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index a492d47e8..600c4275d 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -80,7 +80,7 @@ static bool IsBinarySTL(const char* buffer, unsigned int fileSize) { return false; } - char *facecount_pos = buffer + 80; + const char *facecount_pos = buffer + 80; uint32_t faceCount( 0 ); ::memcpy( &faceCount, facecount_pos, sizeof( uint32_t ) ); const uint32_t expectedBinaryFileSize = faceCount * 50 + 84; From f41ed2f41fd09e0680e72bbe84f8b899fd802540 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 8 Nov 2017 01:07:04 +0100 Subject: [PATCH 289/490] closes https://github.com/assimp/assimp/issues/1251: use correct lookup for utf32. --- code/STEPFileEncoding.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/STEPFileEncoding.cpp b/code/STEPFileEncoding.cpp index f9a9dd1ce..7204f802b 100644 --- a/code/STEPFileEncoding.cpp +++ b/code/STEPFileEncoding.cpp @@ -334,7 +334,7 @@ bool STEP::StringToUTF8(std::string& s) size_t j = basei, jend = s.size()-3; for (; j < jend; ++j) { - if (s[j] == '\\' && s[j] == 'X' && s[j] == '0' && s[j] == '\\') { + if (s[j] == '\\' && s[j+1] == 'X' && s[j+2] == '0' && s[j+3] == '\\') { break; } } From fe55bc9996a420ff52495ea7ab58d61c62a88937 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 8 Nov 2017 18:34:16 +0100 Subject: [PATCH 290/490] Update ColladaExporter.cpp fix build --- code/ColladaExporter.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/code/ColladaExporter.cpp b/code/ColladaExporter.cpp index 94a0b1539..aa4ea2fc5 100644 --- a/code/ColladaExporter.cpp +++ b/code/ColladaExporter.cpp @@ -1309,8 +1309,9 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) // Combine the above transformations aiMatrix4x4 mat = TranslationM * RotationM * ScalingM; - for( uint j = 0; j < 4; ++j) + for( size_t j = 0; j < 4; ++j) { keyframes.insert(keyframes.end(), mat[j], mat[j] + 4); + } } WriteFloatArray( node_idstr, FloatType_Mat4x4, (const ai_real*) keyframes.data(), keyframes.size() / 16); @@ -1337,8 +1338,9 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) // source array mOutput << startstr << " "; - for( size_t a = 0; a < names.size(); ++a ) + for( size_t a = 0; a < names.size(); ++a ) { mOutput << names[a] << " "; + } mOutput << "" << endstr; mOutput << startstr << "" << endstr; From cdfd4b9702ebf053d00321fe5b47ab8a35695866 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 8 Nov 2017 20:44:53 +0100 Subject: [PATCH 291/490] closes https://github.com/assimp/assimp/issues/1315: check in exporting against out-of-bounds-access . --- code/ObjExporter.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/code/ObjExporter.cpp b/code/ObjExporter.cpp index fffafa328..6dd68b8f5 100644 --- a/code/ObjExporter.cpp +++ b/code/ObjExporter.cpp @@ -258,7 +258,6 @@ void ObjExporter::WriteMaterialFile() } } -// ------------------------------------------------------------------------------------------------ void ObjExporter::WriteGeometryFile(bool noMtl) { WriteHeader(mOutput); if (!noMtl) @@ -280,8 +279,10 @@ void ObjExporter::WriteGeometryFile(bool noMtl) { mOutput << "# " << vp.size() << " vertex positions and colors" << endl; size_t colIdx = 0; for ( const aiVector3D& v : vp ) { - mOutput << "v " << v.x << " " << v.y << " " << v.z << " " << vc[ colIdx ].r << " " << vc[ colIdx ].g << " " << vc[ colIdx ].b << endl; - colIdx++; + if ( colIdx < vc.size() ) { + mOutput << "v " << v.x << " " << v.y << " " << v.z << " " << vc[ colIdx ].r << " " << vc[ colIdx ].g << " " << vc[ colIdx ].b << endl; + } + ++colIdx; } } mOutput << endl; From 36475bf868feef74a8807e8f99edcd002878f8b0 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 8 Nov 2017 20:50:16 +0100 Subject: [PATCH 292/490] closes https://github.com/assimp/assimp/issues/1292: export class subdivision --- code/Subdivision.h | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/code/Subdivision.h b/code/Subdivision.h index b8ce228d2..d06bc09d6 100644 --- a/code/Subdivision.h +++ b/code/Subdivision.h @@ -40,10 +40,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file Defines a helper class to evaluate subdivision surfaces.*/ +#pragma once #ifndef AI_SUBDISIVION_H_INC #define AI_SUBDISIVION_H_INC #include +#include + struct aiMesh; namespace Assimp { @@ -52,8 +55,7 @@ namespace Assimp { /** Helper class to evaluate subdivision surfaces. Different algorithms * are provided for choice. */ // ------------------------------------------------------------------------------ -class Subdivider -{ +class ASSIMP_API Subdivider { public: /** Enumerates all supported subvidision algorithms */ @@ -61,12 +63,7 @@ public: CATMULL_CLARKE = 0x1 }; -public: - - virtual ~Subdivider() { - } - -public: + virtual ~Subdivider(); // --------------------------------------------------------------- /** Create a subdivider of a specific type @@ -119,9 +116,13 @@ public: unsigned int num, bool discard_input = false) = 0; -private: }; +inline +Subdivider::~Subdivider() { + // empty +} + } // end namespace Assimp From 9ec117d0bcd97b6e0fc21083c9d995ebc4309527 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Tue, 7 Nov 2017 15:13:01 -0500 Subject: [PATCH 293/490] Fix export of deleted meshes; Add LazyDict::Remove method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When I was merging a node’s multiple meshes into that node’s first mesh’s primitives, I was deleting the merged meshes from the node. However, I wasn’t deleting the merged meshes from the mAsset->meshes Dict, causing the gltf2 export to contain extra unreferenced meshes and duplicate primitives. This new code adds a new method to LazyDict, which removes the object from it, taking care to update indexes of the subsequent objects. This change also requires that `Ref`s of `Mesh`es (stored in node->meshes) have their indexes updated. --- code/glTF2Asset.h | 2 ++ code/glTF2Asset.inl | 44 ++++++++++++++++++++++++++++++++++++++++++ code/glTF2Exporter.cpp | 28 +++++++++++++++++++++++++-- 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index a98fe5ab2..c9390d8d4 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -971,6 +971,8 @@ namespace glTF2 Ref Create(const std::string& id) { return Create(id.c_str()); } + unsigned int Remove(const char* id); + inline unsigned int Size() const { return unsigned(mObjs.size()); } diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 8b50fa1d3..c7d0cdc54 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -193,6 +193,50 @@ inline void LazyDict::DetachFromDocument() mDict = 0; } +template +unsigned int LazyDict::Remove(const char* id) +{ + id = T::TranslateId(mAsset, id); + + typename IdDict::iterator it = mObjsById.find(id); + + if (it == mObjsById.end()) { + throw DeadlyExportError("GLTF: Object with id \"" + std::string(id) + "\" is not found"); + } + + const int index = it->second; + + mAsset.mUsedIds[id] = false; + mObjsById.erase(id); + mObjsByOIndex.erase(index); + mObjs.erase(mObjs.begin() + index); + + //update index of object in mObjs; + for (size_t i = index; i < mObjs.size(); ++i) { + T *obj = mObjs[i]; + + obj->index = i; + } + + for (IdDict::iterator it = mObjsById.begin(); it != mObjsById.end(); ++it) { + if (it->second <= index) { + continue; + } + + mObjsById[it->first] = it->second - 1; + } + + for (Dict::iterator it = mObjsByOIndex.begin(); it != mObjsByOIndex.end(); ++it) { + if (it->second <= index) { + continue; + } + + mObjsByOIndex[it->first] = it->second - 1; + } + + return index; +} + template Ref LazyDict::Retrieve(unsigned int i) { diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index d8cff897c..e71d949be 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -800,9 +800,33 @@ void glTF2Exporter::MergeMeshes() for (unsigned int m = nMeshes - 1; m >= 1; --m) { Ref mesh = node->meshes.at(m); - firstMesh->primitives.insert(firstMesh->primitives.end(), mesh->primitives.begin(), mesh->primitives.end()); + //append this mesh's primitives to the first mesh's primitives + firstMesh->primitives.insert( + firstMesh->primitives.end(), + mesh->primitives.begin(), + mesh->primitives.end() + ); - node->meshes.erase(node->meshes.begin() + m); + //remove the mesh from the list of meshes + unsigned int removedIndex = mAsset->meshes.Remove(mesh->id.c_str()); + + //find the presence of the removed mesh in other nodes + for (unsigned int nn = 0; nn < mAsset->nodes.Size(); ++nn) { + Ref node = mAsset->nodes.Get(nn); + + for (unsigned int mm = 0; mm < node->meshes.size(); ++mm) { + Ref& meshRef = node->meshes.at(mm); + unsigned int meshIndex = meshRef.GetIndex(); + + if (meshIndex == removedIndex) { + node->meshes.erase(node->meshes.begin() + mm); + } else if (meshIndex > removedIndex) { + Ref newMeshRef = mAsset->meshes.Get(meshIndex - 1); + + meshRef = newMeshRef; + } + } + } } //since we were looping backwards, reverse the order of merged primitives to their original order From 30e06f7437d3da72b38e452283e163890215fec4 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 9 Nov 2017 21:31:16 +0100 Subject: [PATCH 294/490] closes https://github.com/assimp/assimp/issues/216: check the area of a triangle to check if its degenerated or not. --- code/FBXBinaryTokenizer.cpp | 1 - code/FindDegenerates.cpp | 101 ++++++++++++++++++++++++------------ code/STLLoader.cpp | 6 --- code/XFileImporter.h | 5 +- 4 files changed, 68 insertions(+), 45 deletions(-) diff --git a/code/FBXBinaryTokenizer.cpp b/code/FBXBinaryTokenizer.cpp index 9ae38386b..519f2e176 100644 --- a/code/FBXBinaryTokenizer.cpp +++ b/code/FBXBinaryTokenizer.cpp @@ -422,7 +422,6 @@ bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor, return true; } - } // ------------------------------------------------------------------------------------------------ diff --git a/code/FindDegenerates.cpp b/code/FindDegenerates.cpp index 32a09f0c0..5b321312a 100644 --- a/code/FindDegenerates.cpp +++ b/code/FindDegenerates.cpp @@ -56,98 +56,131 @@ using namespace Assimp; // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer FindDegeneratesProcess::FindDegeneratesProcess() -: configRemoveDegenerates (false) -{} +: configRemoveDegenerates (false) { + // empty +} // ------------------------------------------------------------------------------------------------ // Destructor, private as well -FindDegeneratesProcess::~FindDegeneratesProcess() -{ +FindDegeneratesProcess::~FindDegeneratesProcess() { // nothing to do here } // ------------------------------------------------------------------------------------------------ // Returns whether the processing step is present in the given flag field. -bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const -{ +bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const { return 0 != (pFlags & aiProcess_FindDegenerates); } // ------------------------------------------------------------------------------------------------ // Setup import configuration -void FindDegeneratesProcess::SetupProperties(const Importer* pImp) -{ +void FindDegeneratesProcess::SetupProperties(const Importer* pImp) { // Get the current value of AI_CONFIG_PP_FD_REMOVE configRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0)); } // ------------------------------------------------------------------------------------------------ // Executes the post processing step on the given imported data. -void FindDegeneratesProcess::Execute( aiScene* pScene) -{ +void FindDegeneratesProcess::Execute( aiScene* pScene) { DefaultLogger::get()->debug("FindDegeneratesProcess begin"); for (unsigned int i = 0; i < pScene->mNumMeshes;++i){ - ExecuteOnMesh( pScene->mMeshes[i]); + ExecuteOnMesh( pScene->mMeshes[ i ] ); } DefaultLogger::get()->debug("FindDegeneratesProcess finished"); } +static ai_real heron( ai_real a, ai_real b, ai_real c ) { + ai_real s = (a + b + c) / 2; + ai_real area = pow((s * ( s - a ) * ( s - b ) * ( s - c ) ), 0.5 ); + return area; +} + +static ai_real distance3D( const aiVector3D &vA, aiVector3D &vB ) { + const ai_real lx = ( vB.x - vA.x ); + const ai_real ly = ( vB.y - vA.y ); + const ai_real lz = ( vB.z - vA.z ); + ai_real a = lx*lx + ly*ly + lz*lz; + ai_real d = pow( a, 0.5 ); + + return d; +} + +static ai_real calculateAreaOfTriangle( const aiFace& face, aiMesh* mesh ) { + ai_real area = 0; + + aiVector3D vA( mesh->mVertices[ face.mIndices[ 0 ] ] ); + aiVector3D vB( mesh->mVertices[ face.mIndices[ 1 ] ] ); + aiVector3D vC( mesh->mVertices[ face.mIndices[ 2 ] ] ); + + ai_real a( distance3D( vA, vB ) ); + ai_real b( distance3D( vB, vC ) ); + ai_real c( distance3D( vC, vA ) ); + area = heron( a, b, c ); + + return area; +} + // ------------------------------------------------------------------------------------------------ // Executes the post processing step on the given imported mesh -void FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) -{ +void FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) { mesh->mPrimitiveTypes = 0; std::vector remove_me; - if (configRemoveDegenerates) - remove_me.resize(mesh->mNumFaces,false); + if (configRemoveDegenerates) { + remove_me.resize( mesh->mNumFaces, false ); + } unsigned int deg = 0, limit; - for (unsigned int a = 0; a < mesh->mNumFaces; ++a) - { + for ( unsigned int a = 0; a < mesh->mNumFaces; ++a ) { aiFace& face = mesh->mFaces[a]; bool first = true; // check whether the face contains degenerated entries - for (unsigned int i = 0; i < face.mNumIndices; ++i) - { + for (unsigned int i = 0; i < face.mNumIndices; ++i) { // Polygons with more than 4 points are allowed to have double points, that is // simulating polygons with holes just with concave polygons. However, // double points may not come directly after another. limit = face.mNumIndices; - if (face.mNumIndices > 4) - limit = std::min(limit,i+2); + if (face.mNumIndices > 4) { + limit = std::min( limit, i+2 ); + } - for (unsigned int t = i+1; t < limit; ++t) - { - if (mesh->mVertices[face.mIndices[i]] == mesh->mVertices[face.mIndices[t]]) - { + for (unsigned int t = i+1; t < limit; ++t) { + if (mesh->mVertices[face.mIndices[ i ] ] == mesh->mVertices[ face.mIndices[ t ] ]) { // we have found a matching vertex position // remove the corresponding index from the array - --face.mNumIndices;--limit; - for (unsigned int m = t; m < face.mNumIndices; ++m) - { - face.mIndices[m] = face.mIndices[m+1]; + --face.mNumIndices; + --limit; + for (unsigned int m = t; m < face.mNumIndices; ++m) { + face.mIndices[ m ] = face.mIndices[ m+1 ]; } --t; // NOTE: we set the removed vertex index to an unique value // to make sure the developer gets notified when his // application attemps to access this data. - face.mIndices[face.mNumIndices] = 0xdeadbeef; + face.mIndices[ face.mNumIndices ] = 0xdeadbeef; - if(first) - { + if(first) { ++deg; first = false; } - if (configRemoveDegenerates) { - remove_me[a] = true; + if ( configRemoveDegenerates ) { + remove_me[ a ] = true; goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby! } } } + ai_real area = calculateAreaOfTriangle( face, mesh ); + if ( area < 1e-6 ) { + if ( configRemoveDegenerates ) { + remove_me[ a ] = true; + goto evil_jump_outside; + } + + // todo: check for index which is corrupt. + } } // We need to update the primitive flags array of the mesh. diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index 600c4275d..41f8aad03 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -207,12 +207,6 @@ void STLImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS throw DeadlyImportError( "Failed to determine STL storage representation for " + pFile + "."); } - // add all created meshes to the single node - /*pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; - pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; - for (unsigned int i = 0; i < pScene->mNumMeshes; i++) - pScene->mRootNode->mMeshes[i] = i; - */ // create a single default material, using a white diffuse color for consistency with // other geometric types (e.g., PLY). aiMaterial* pcMat = new aiMaterial(); diff --git a/code/XFileImporter.h b/code/XFileImporter.h index 528dcb851..0fa6b52eb 100644 --- a/code/XFileImporter.h +++ b/code/XFileImporter.h @@ -65,14 +65,11 @@ struct Node; /** The XFileImporter is a worker class capable of importing a scene from a * DirectX file .x */ -class XFileImporter : public BaseImporter -{ +class XFileImporter : public BaseImporter { public: XFileImporter(); ~XFileImporter(); - -public: // ------------------------------------------------------------------- /** Returns whether the class can handle the format of the given file. * See BaseImporter::CanRead() for details. */ From ad2ff9fd713e9bf7a21bf552bec4fdd6e5cee9db Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 9 Nov 2017 21:40:10 +0100 Subject: [PATCH 295/490] check for area test if the face is a triangle. --- code/FindDegenerates.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/code/FindDegenerates.cpp b/code/FindDegenerates.cpp index 5b321312a..83dce22ba 100644 --- a/code/FindDegenerates.cpp +++ b/code/FindDegenerates.cpp @@ -172,14 +172,16 @@ void FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) { } } } - ai_real area = calculateAreaOfTriangle( face, mesh ); - if ( area < 1e-6 ) { - if ( configRemoveDegenerates ) { - remove_me[ a ] = true; - goto evil_jump_outside; - } + if ( face.mNumIndices == 3 ) { + ai_real area = calculateAreaOfTriangle( face, mesh ); + if ( area < 1e-6 ) { + if ( configRemoveDegenerates ) { + remove_me[ a ] = true; + goto evil_jump_outside; + } - // todo: check for index which is corrupt. + // todo: check for index which is corrupt. + } } } From 12dbbd4ce99e0c8d000a6ee33603cbe1280c0166 Mon Sep 17 00:00:00 2001 From: Unknown Date: Thu, 9 Nov 2017 17:19:26 -0500 Subject: [PATCH 296/490] Misc. typos Some are doxy comments, some are just trivial source comment typos. Found using `codespell -q 3 --skip="./contrib" -I ../assimp-whitelist.txt` whereby whitelist contained: ``` childs iff lod nto ot whitespaces ``` --- cmake-modules/CoverallsGenerateGcov.cmake | 4 ++-- code/AMFImporter.hpp | 4 ++-- code/AMFImporter_Node.hpp | 2 +- code/ColladaExporter.cpp | 2 +- code/ColladaHelper.h | 2 +- code/ColladaLoader.cpp | 2 +- code/ColladaParser.cpp | 2 +- code/ColladaParser.h | 2 +- code/LWOMaterial.cpp | 2 +- code/LWSLoader.cpp | 2 +- code/ObjExporter.cpp | 2 +- code/OgreMaterial.cpp | 2 +- code/TextureTransform.cpp | 2 +- code/X3DExporter.hpp | 4 ++-- code/X3DImporter.hpp | 6 +++--- code/glTF2Exporter.cpp | 2 +- code/glTFExporter.cpp | 2 +- doc/Doxyfile.in | 2 +- doc/dox.h | 10 +++++----- include/assimp/config.h.in | 2 +- port/PyAssimp/pyassimp/structs.py | 2 +- port/PyAssimp/scripts/3d_viewer.py | 2 +- port/PyAssimp/scripts/3d_viewer_py3.py | 2 +- port/PyAssimp/scripts/transformations.py | 2 +- port/dAssimp/assimp/mesh.d | 2 +- port/jassimp/jassimp-native/src/jassimp.cpp | 2 +- port/swig/assimp.i | 2 +- samples/SimpleAssimpViewX/MyDocument.mm | 2 +- samples/glut/GL/glut.h | 4 ++-- test/models-nonbsd/BLEND/fleurOptonl.source.txt | 2 +- test/models/AMF/README | 2 +- tools/assimp_qt_viewer/glview.cpp | 2 +- tools/assimp_qt_viewer/glview.hpp | 6 +++--- tools/assimp_qt_viewer/mainwindow.cpp | 2 +- tools/assimp_qt_viewer/mainwindow.hpp | 8 ++++---- tools/assimp_view/assimp_view.rc | 2 +- 36 files changed, 51 insertions(+), 51 deletions(-) diff --git a/cmake-modules/CoverallsGenerateGcov.cmake b/cmake-modules/CoverallsGenerateGcov.cmake index ba506e3d4..104737164 100644 --- a/cmake-modules/CoverallsGenerateGcov.cmake +++ b/cmake-modules/CoverallsGenerateGcov.cmake @@ -310,7 +310,7 @@ foreach (GCOV_FILE ${GCOV_FILES}) message("MD5: ${GCOV_SRC_PATH} = ${GCOV_CONTENTS_MD5}") # Loads the gcov file as a list of lines. - # (We first open the file and replace all occurences of [] with _ + # (We first open the file and replace all occurrences of [] with _ # because CMake will fail to parse a line containing unmatched brackets... # also the \ to escaped \n in macros screws up things.) # https://public.kitware.com/Bug/view.php?id=15369 @@ -329,7 +329,7 @@ foreach (GCOV_FILE ${GCOV_FILES}) # Instead of trying to parse the source from the # gcov file, simply read the file contents from the source file. # (Parsing it from the gcov is hard because C-code uses ; in many places - # which also happens to be the same as the CMake list delimeter). + # which also happens to be the same as the CMake list delimiter). file(READ ${GCOV_SRC_PATH} GCOV_FILE_SOURCE) string(REPLACE "\\" "\\\\" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}") diff --git a/code/AMFImporter.hpp b/code/AMFImporter.hpp index 561ec3c8f..47ddc073b 100644 --- a/code/AMFImporter.hpp +++ b/code/AMFImporter.hpp @@ -249,7 +249,7 @@ private: /// \fn size_t PostprocessHelper_GetTextureID_Or_Create(const std::string& pID_R, const std::string& pID_G, const std::string& pID_B, const std::string& pID_A) /// Return converted texture ID which related to specified source textures ID's. If converted texture does not exist then it will be created and ID on new - /// converted texture will be returned. Convertion: set of textures from \ref CAMFImporter_NodeElement_Texture to one \ref SPP_Texture and place it + /// converted texture will be returned. Conversion: set of textures from \ref CAMFImporter_NodeElement_Texture to one \ref SPP_Texture and place it /// to converted textures list. /// Any of source ID's can be absent(empty string) or even one ID only specified. But at least one ID must be specified. /// \param [in] pID_R - ID of source "red" texture. @@ -378,7 +378,7 @@ private: void XML_CheckNode_MustHaveChildren(); /// \fn bool XML_CheckNode_NameEqual(const std::string& pNodeName) - /// Chek if current node name is equal to pNodeName. + /// Check if current node name is equal to pNodeName. /// \param [in] pNodeName - name for checking. /// return true if current node name is equal to pNodeName, else - false. bool XML_CheckNode_NameEqual(const std::string& pNodeName) { return mReader->getNodeName() == pNodeName; } diff --git a/code/AMFImporter_Node.hpp b/code/AMFImporter_Node.hpp index cb8b0b66d..522e6ccca 100644 --- a/code/AMFImporter_Node.hpp +++ b/code/AMFImporter_Node.hpp @@ -137,7 +137,7 @@ struct CAMFImporter_NodeElement_Instance : public CAMFImporter_NodeElement { /****************** Variables ******************/ - std::string ObjectID;///< ID of object for instanciation. + std::string ObjectID;///< ID of object for instantiation. /// \var Delta - The distance of translation in the x, y, or z direction, respectively, in the referenced object's coordinate system, to /// create an instance of the object in the current constellation. aiVector3D Delta; diff --git a/code/ColladaExporter.cpp b/code/ColladaExporter.cpp index aa4ea2fc5..ffef6f067 100644 --- a/code/ColladaExporter.cpp +++ b/code/ColladaExporter.cpp @@ -1561,7 +1561,7 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode) for( size_t a = 0; a < pNode->mNumMeshes; ++a ) { const aiMesh* mesh = mScene->mMeshes[pNode->mMeshes[a]]; - // do not instanciate mesh if empty. I wonder how this could happen + // do not instantiate mesh if empty. I wonder how this could happen if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 ) continue; diff --git a/code/ColladaHelper.h b/code/ColladaHelper.h index 8ccd6cafe..e691a6062 100644 --- a/code/ColladaHelper.h +++ b/code/ColladaHelper.h @@ -302,7 +302,7 @@ struct Accessor size_t mOffset; // in number of values size_t mStride; // Stride in number of values std::vector mParams; // names of the data streams in the accessors. Empty string tells to ignore. - size_t mSubOffset[4]; // Suboffset inside the object for the common 4 elements. For a vector, thats XYZ, for a color RGBA and so on. + size_t mSubOffset[4]; // Suboffset inside the object for the common 4 elements. For a vector, that's XYZ, for a color RGBA and so on. // For example, SubOffset[0] denotes which of the values inside the object is the vector X component. std::string mSource; // URL of the source array mutable const Data* mData; // Pointer to the source array, if resolved. NULL else diff --git a/code/ColladaLoader.cpp b/code/ColladaLoader.cpp index ccf79ed62..d2141c374 100644 --- a/code/ColladaLoader.cpp +++ b/code/ColladaLoader.cpp @@ -1619,7 +1619,7 @@ void ColladaLoader::FillMaterials( const ColladaParser& pParser, aiScene* /*pSce mat.AddProperty( &effect.mRefractIndex, 1, AI_MATKEY_REFRACTI); // transparency, a very hard one. seemingly not all files are following the - // specification here (1.0 transparency => completly opaque)... + // specification here (1.0 transparency => completely opaque)... // therefore, we let the opportunity for the user to manually invert // the transparency if necessary and we add preliminary support for RGB_ZERO mode if(effect.mTransparency >= 0.f && effect.mTransparency <= 1.f) { diff --git a/code/ColladaParser.cpp b/code/ColladaParser.cpp index 5ff581b91..2fe0f1e0a 100644 --- a/code/ColladaParser.cpp +++ b/code/ColladaParser.cpp @@ -224,7 +224,7 @@ void ColladaParser::ReadStructure() } // ------------------------------------------------------------------------------------------------ -// Reads asset informations such as coordinate system informations and legal blah +// Reads asset information such as coordinate system information and legal blah void ColladaParser::ReadAssetInfo() { if( mReader->isEmptyElement()) diff --git a/code/ColladaParser.h b/code/ColladaParser.h index b34974470..941ab8414 100644 --- a/code/ColladaParser.h +++ b/code/ColladaParser.h @@ -77,7 +77,7 @@ namespace Assimp /** Reads the structure of the file */ void ReadStructure(); - /** Reads asset informations such as coordinate system informations and legal blah */ + /** Reads asset information such as coordinate system information and legal blah */ void ReadAssetInfo(); /** Reads the animation library */ diff --git a/code/LWOMaterial.cpp b/code/LWOMaterial.cpp index e2ba894af..621a2edc6 100644 --- a/code/LWOMaterial.cpp +++ b/code/LWOMaterial.cpp @@ -483,7 +483,7 @@ void LWOImporter::FindVCChannels(const LWO::Surface& surf, LWO::SortedRep& sorte const LWO::VColorChannel& vc = layer.mVColorChannels[i]; if (surf.mVCMap == vc.name) { - // The vertex color map is explicitely requested by the surface so we need to take special care of it + // The vertex color map is explicitly requested by the surface so we need to take special care of it for (unsigned int a = 0; a < std::min(next,AI_MAX_NUMBER_OF_COLOR_SETS-1u); ++a) { out[a+1] = out[a]; } diff --git a/code/LWSLoader.cpp b/code/LWSLoader.cpp index 302228556..2017cdb9d 100644 --- a/code/LWSLoader.cpp +++ b/code/LWSLoader.cpp @@ -471,7 +471,7 @@ void LWSImporter::BuildGraph(aiNode* nd, LWS::NodeDesc& src, std::vector 3 && in[1] == ':'&& in[2] != '\\' && in[2] != '/') { diff --git a/code/ObjExporter.cpp b/code/ObjExporter.cpp index 6dd68b8f5..a8bf9935a 100644 --- a/code/ObjExporter.cpp +++ b/code/ObjExporter.cpp @@ -155,7 +155,7 @@ std::string ObjExporter :: GetMaterialLibName() // ------------------------------------------------------------------------------------------------ std::string ObjExporter::GetMaterialLibFileName() { - // Remove existing .obj file extention so that the final material file name will be fileName.mtl and not fileName.obj.mtl + // Remove existing .obj file extension so that the final material file name will be fileName.mtl and not fileName.obj.mtl size_t lastdot = filename.find_last_of('.'); if (lastdot != std::string::npos) return filename.substr(0, lastdot) + MaterialExt; diff --git a/code/OgreMaterial.cpp b/code/OgreMaterial.cpp index dfb77ffef..1abca2cf1 100644 --- a/code/OgreMaterial.cpp +++ b/code/OgreMaterial.cpp @@ -258,7 +258,7 @@ aiMaterial* OgreImporter::ReadMaterial(const std::string &pFile, Assimp::IOSyste ReadTechnique(Trim(techniqueName), ss, material); } - // Read informations from a custom material + // Read information from a custom material /** @todo This "set $x y" does not seem to be a official Ogre material system feature. Materials can inherit other materials and override texture units by using the (unique) parent texture unit name in your cloned material. diff --git a/code/TextureTransform.cpp b/code/TextureTransform.cpp index 76f0ce58c..30953113e 100644 --- a/code/TextureTransform.cpp +++ b/code/TextureTransform.cpp @@ -317,7 +317,7 @@ void TextureTransformStep::Execute( aiScene* pScene) info.lockedPos = AI_TT_UV_IDX_LOCK_TBD; } - // Get all coresponding meshes + // Get all corresponding meshes for (unsigned int n = 0; n < pScene->mNumMeshes;++n) { aiMesh* mesh = pScene->mMeshes[n]; if (mesh->mMaterialIndex != i || !mesh->mTextureCoords[0]) diff --git a/code/X3DExporter.hpp b/code/X3DExporter.hpp index bf1e72218..45f302d8d 100644 --- a/code/X3DExporter.hpp +++ b/code/X3DExporter.hpp @@ -27,7 +27,7 @@ namespace Assimp /// /// Pay attention that X3D is format for interactive graphic and simulations for web browsers. aiScene can not contain all features of the X3D format. /// Also, aiScene contain rasterized-like data. For example, X3D can describe circle all cylinder with one tag, but aiScene contain result of tesselation: -/// vertices, faces etc. Yes, you can use algorithm for detecting figures or shapes, but thats not good idea at all. +/// vertices, faces etc. Yes, you can use algorithm for detecting figures or shapes, but that's not a good idea at all. /// /// Supported nodes: /// Core component: @@ -96,7 +96,7 @@ private: aiMatrix4x4 Matrix_GlobalToCurrent(const aiNode& pNode) const; /// \fn void AttrHelper_CommaToPoint(std::string& pStringWithComma) - /// Convert commas in string to points. Thats need because "std::to_string" result depend on locale (regional settings). + /// Convert commas in string to points. That's needed because "std::to_string" result depends on locale (regional settings). /// \param [in, out] pStringWithComma - reference to string, which must be modified. void AttrHelper_CommaToPoint(std::string& pStringWithComma) { for(char& c: pStringWithComma) { if(c == ',') c = '.'; } } diff --git a/code/X3DImporter.hpp b/code/X3DImporter.hpp index 2d2d41fdb..468db5f3e 100644 --- a/code/X3DImporter.hpp +++ b/code/X3DImporter.hpp @@ -176,7 +176,7 @@ namespace Assimp { /// Ignored attributes: "creaseAngle", "convex", "solid". /// /// Texture coordinates generating: only for Sphere, Cone, Cylinder. In all other case used PLANE mapping. -/// It's better that Assimp main code has powerfull texture coordinates generator. Then is not needed to +/// It's better that Assimp main code has powerful texture coordinates generator. Then is not needed to /// duplicate this code in every importer. /// /// Lighting limitations. @@ -401,10 +401,10 @@ private: /************** Functions: XML set *************/ /***********************************************/ - /// Chek if current node is empty: . If not then exception will throwed. + /// Check if current node is empty: . If not then exception will throwed. void XML_CheckNode_MustBeEmpty(); - /// Chek if current node name is equal to pNodeName. + /// Check if current node name is equal to pNodeName. /// \param [in] pNodeName - name for checking. /// return true if current node name is equal to pNodeName, else - false. bool XML_CheckNode_NameEqual(const std::string& pNodeName) { return mReader->getNodeName() == pNodeName; } diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index cd6ca4630..22e003a3f 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -56,7 +56,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -// Header files, standart library. +// Header files, standard library. #include #include diff --git a/code/glTFExporter.cpp b/code/glTFExporter.cpp index 39886aaad..d88081884 100644 --- a/code/glTFExporter.cpp +++ b/code/glTFExporter.cpp @@ -56,7 +56,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -// Header files, standart library. +// Header files, standard library. #include #include diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index cb629a985..8d51259a8 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -1742,7 +1742,7 @@ UML_LOOK = NO # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more -# managable. Set this to 0 for no limit. Note that the threshold may be +# manageable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 diff --git a/doc/dox.h b/doc/dox.h index 46ca23251..2672adcb5 100644 --- a/doc/dox.h +++ b/doc/dox.h @@ -60,7 +60,7 @@ that it has not been implemented yet and some (most ...) formats lack proper spe Stanford Ply ( *.ply )
TrueSpace ( *.cob, *.scn )2

-See the @link importer_notes Importer Notes Page @endlink for informations, what a specific importer can do and what not. +See the @link importer_notes Importer Notes Page @endlink for information, what a specific importer can do and what not. Note that although this paper claims to be the official documentation, http://assimp.sourceforge.net/main_features_formats.html
is usually the most up-to-date list of file formats supported by the library.
@@ -81,7 +81,7 @@ formats handle the required endian conversion correctly, so large parts of the l The assimp linker library and viewer application are provided under the BSD 3-clause license. This basically means that you are free to use it in open- or closed-source projects, for commercial or non-commercial purposes as you like -as long as you retain the license informations and take own responsibility for what you do with it. For details see +as long as you retain the license information and take own responsibility for what you do with it. For details see the LICENSE file. You can find test models for almost all formats in the /test/models directory. Beware, they're *free*, @@ -1420,7 +1420,7 @@ IFC file properties (IfcPropertySet) are kept as per-node metadata, see aiNode:: This section contains implementations notes for the OgreXML importer. @subsection overview Overview -Ogre importer is currently optimized for the Blender Ogre exporter, because thats the only one that I use. You can find the Blender Ogre exporter at: http://www.ogre3d.org/forums/viewtopic.php?f=8&t=45922 +Ogre importer is currently optimized for the Blender Ogre exporter, because that's the only one that I use. You can find the Blender Ogre exporter at: http://www.ogre3d.org/forums/viewtopic.php?f=8&t=45922 @subsection what What will be loaded? @@ -1434,7 +1434,7 @@ Skeleton: Skeleton with Bone hierarchy (Position and Rotation, but no Scaling in animations with rotation, translation and scaling keys. @subsection export_Blender How to export Files from Blender -You can find informations about how to use the Ogreexporter by your own, so here are just some options that you need, so the assimp +You can find information about how to use the Ogreexporter by your own, so here are just some options that you need, so the assimp importer will load everything correctly: - Use either "Rendering Material" or "Custom Material" see @ref material - do not use "Flip Up Axies to Y" @@ -1543,7 +1543,7 @@ Done! Please, share your loader that everyone can profit from it! @section properties Properties -You can use properties to chance the behavior of you importer. In order to do so, you have to overide BaseImporter::SetupProperties, and specify +You can use properties to chance the behavior of you importer. In order to do so, you have to override BaseImporter::SetupProperties, and specify you custom properties in config.h. Just have a look to the other AI_CONFIG_IMPORT_* defines and you will understand, how it works. The properties can be set with Importer::SetProperty***() and can be accessed in your SetupProperties function with Importer::GetProperty***(). You can diff --git a/include/assimp/config.h.in b/include/assimp/config.h.in index 29b9d5870..bafd96189 100644 --- a/include/assimp/config.h.in +++ b/include/assimp/config.h.in @@ -903,7 +903,7 @@ enum aiComponent /** @brief Set the tessellation for IFC cylindrical shapes. * * This is used by the IFC importer to determine the tessellation parameter - * for cylindrical shapes, i.e. the number of segments used to aproximate a circle. + * for cylindrical shapes, i.e. the number of segments used to approximate a circle. * @note The default value is AI_IMPORT_IFC_DEFAULT_CYLINDRICAL_TESSELLATION and the * accepted values are in range [3, 180]. * Property type: Integer. diff --git a/port/PyAssimp/pyassimp/structs.py b/port/PyAssimp/pyassimp/structs.py index 84fcfb7a0..edae77d8d 100644 --- a/port/PyAssimp/pyassimp/structs.py +++ b/port/PyAssimp/pyassimp/structs.py @@ -206,7 +206,7 @@ class MeshKey(Structure): ("mTime", c_double), # Index into the aiMesh::mAnimMeshes array of the - # mesh coresponding to the + # mesh corresponding to the #aiMeshAnim hosting this # key frame. The referenced anim mesh is evaluated # according to the rules defined in the docs for diff --git a/port/PyAssimp/scripts/3d_viewer.py b/port/PyAssimp/scripts/3d_viewer.py index 9aae25efe..08a62660b 100755 --- a/port/PyAssimp/scripts/3d_viewer.py +++ b/port/PyAssimp/scripts/3d_viewer.py @@ -895,7 +895,7 @@ class PyAssimp3DViewer: aspect = camera.aspect u = 0.1 # unit size (in m) - l = 3 * u # lenght of the camera cone + l = 3 * u # length of the camera cone f = 3 * u # aperture of the camera cone glPushMatrix() diff --git a/port/PyAssimp/scripts/3d_viewer_py3.py b/port/PyAssimp/scripts/3d_viewer_py3.py index 4e4ecebe8..4c88edc7b 100755 --- a/port/PyAssimp/scripts/3d_viewer_py3.py +++ b/port/PyAssimp/scripts/3d_viewer_py3.py @@ -897,7 +897,7 @@ class PyAssimp3DViewer: aspect = camera.aspect u = 0.1 # unit size (in m) - l = 3 * u # lenght of the camera cone + l = 3 * u # length of the camera cone f = 3 * u # aperture of the camera cone glPushMatrix() diff --git a/port/PyAssimp/scripts/transformations.py b/port/PyAssimp/scripts/transformations.py index 6d6f19e5b..bf0cac9e3 100644 --- a/port/PyAssimp/scripts/transformations.py +++ b/port/PyAssimp/scripts/transformations.py @@ -583,7 +583,7 @@ def clip_matrix(left, right, bottom, top, near, far, perspective=False): orthographic canonical view volume (a box). Homogeneous coordinates transformed by the perspective clip matrix - need to be dehomogenized (devided by w coordinate). + need to be dehomogenized (divided by w coordinate). >>> frustrum = numpy.random.rand(6) >>> frustrum[1] += frustrum[0] diff --git a/port/dAssimp/assimp/mesh.d b/port/dAssimp/assimp/mesh.d index 9f20c9a66..da6193631 100644 --- a/port/dAssimp/assimp/mesh.d +++ b/port/dAssimp/assimp/mesh.d @@ -51,7 +51,7 @@ import assimp.types; extern ( C ) { /* * These limits are required to match the settings Assimp was compiled - * against. Therfore, do not redefine them unless you build the library + * against. Therefore, do not redefine them unless you build the library * from source using the same definitions. */ diff --git a/port/jassimp/jassimp-native/src/jassimp.cpp b/port/jassimp/jassimp-native/src/jassimp.cpp index c2a893896..070dbc95d 100644 --- a/port/jassimp/jassimp-native/src/jassimp.cpp +++ b/port/jassimp/jassimp-native/src/jassimp.cpp @@ -1951,7 +1951,7 @@ error: if (NULL == exception) { - /* thats really a problem because we cannot throw in this case */ + /* that's really a problem because we cannot throw in this case */ env->FatalError("could not throw java.io.IOException"); } gLastErrorString = imp.GetErrorString(); diff --git a/port/swig/assimp.i b/port/swig/assimp.i index d1a078d49..58e1546e7 100644 --- a/port/swig/assimp.i +++ b/port/swig/assimp.i @@ -114,7 +114,7 @@ %include "interface/IOSystem.i" -// We have to "instanciate" the templates used by the ASSSIMP_*_ARRAY macros +// We have to "instantiate" the templates used by the ASSSIMP_*_ARRAY macros // here at the end to avoid running into forward reference issues (SWIG would // spit out the helper functions before the header includes for the element // types otherwise). diff --git a/samples/SimpleAssimpViewX/MyDocument.mm b/samples/SimpleAssimpViewX/MyDocument.mm index be688a6be..cbe034842 100644 --- a/samples/SimpleAssimpViewX/MyDocument.mm +++ b/samples/SimpleAssimpViewX/MyDocument.mm @@ -641,7 +641,7 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink,const CVTimeS { for(MeshHelper* helper in modelMeshes) { - // Set up meterial state. + // Set up material state. glCallList(helper.displayList); } } diff --git a/samples/glut/GL/glut.h b/samples/glut/GL/glut.h index 0e6ddfbfe..86aa5c423 100644 --- a/samples/glut/GL/glut.h +++ b/samples/glut/GL/glut.h @@ -179,7 +179,7 @@ extern void exit(int); glutSpecialUpFunc, glutIgnoreKeyRepeat, glutSetKeyRepeat, glutJoystickFunc, glutForceJoystickFunc (NOT FINALIZED!). **/ -#ifndef GLUT_API_VERSION /* allow this to be overriden */ +#ifndef GLUT_API_VERSION /* allow this to be overridden */ #define GLUT_API_VERSION 3 #endif @@ -219,7 +219,7 @@ extern void exit(int); GLUT_XLIB_IMPLEMENTATION=15 mjk's GLUT 3.7 beta sync'ed with Mesa **/ -#ifndef GLUT_XLIB_IMPLEMENTATION /* Allow this to be overriden. */ +#ifndef GLUT_XLIB_IMPLEMENTATION /* Allow this to be overridden. */ #define GLUT_XLIB_IMPLEMENTATION 15 #endif diff --git a/test/models-nonbsd/BLEND/fleurOptonl.source.txt b/test/models-nonbsd/BLEND/fleurOptonl.source.txt index 13f54a108..b9c58b5d9 100644 --- a/test/models-nonbsd/BLEND/fleurOptonl.source.txt +++ b/test/models-nonbsd/BLEND/fleurOptonl.source.txt @@ -32,7 +32,7 @@ L'utilizzo della Creative Commons non influisce su questo diritto. ************************************************ This model is released under Creative Commons Licence, Attribution 2.0 -for informations see: +for information see: http://creativecommons.org http://creativecommons.org/licenses/by/2.0/ feel free to use and improve it. diff --git a/test/models/AMF/README b/test/models/AMF/README index a64a8508e..7de7cadb2 100644 --- a/test/models/AMF/README +++ b/test/models/AMF/README @@ -1 +1 @@ -Simple models for testing importer. No desription because models are simple and created by hands. +Simple models for testing importer. No description because models are simple and created by hand. diff --git a/tools/assimp_qt_viewer/glview.cpp b/tools/assimp_qt_viewer/glview.cpp index 429f2ca99..5b755d456 100644 --- a/tools/assimp_qt_viewer/glview.cpp +++ b/tools/assimp_qt_viewer/glview.cpp @@ -561,7 +561,7 @@ void CGLView::Enable_Textures(const bool pEnable) } /********************************************************************/ -/*********************** Overrided functions ************************/ +/*********************** Override functions ************************/ /********************************************************************/ void CGLView::initializeGL() diff --git a/tools/assimp_qt_viewer/glview.hpp b/tools/assimp_qt_viewer/glview.hpp index 49207a7ed..ecd7c6b0f 100644 --- a/tools/assimp_qt_viewer/glview.hpp +++ b/tools/assimp_qt_viewer/glview.hpp @@ -249,13 +249,13 @@ private: void Draw_BBox(const SBBox& pBBox); /********************************************************************/ - /*********************** Overrided functions ************************/ + /*********************** Override functions ************************/ /********************************************************************/ protected: void drawCoordSystem(); /// \fn void initializeGL() override - /// Overrided function for initialise OpenGL. + /// Override function to initialise OpenGL. void initializeGL() override; /// \fn void resizeGL(int pWidth, int pHeight) override @@ -264,7 +264,7 @@ protected: void resizeGL(int pWidth, int pHeight) override; /// \fn void paintGL() override - /// Overrided function for rendering. + /// Override function for rendering. void paintGL() override; public: diff --git a/tools/assimp_qt_viewer/mainwindow.cpp b/tools/assimp_qt_viewer/mainwindow.cpp index 9b6e231bd..82ddc7092 100644 --- a/tools/assimp_qt_viewer/mainwindow.cpp +++ b/tools/assimp_qt_viewer/mainwindow.cpp @@ -107,7 +107,7 @@ void MainWindow::LogError(const QString& pMessage) } /********************************************************************/ -/*********************** Overrided functions ************************/ +/*********************** Override functions ************************/ /********************************************************************/ void MainWindow::mousePressEvent(QMouseEvent* pEvent) diff --git a/tools/assimp_qt_viewer/mainwindow.hpp b/tools/assimp_qt_viewer/mainwindow.hpp index 940840aee..b20acb884 100644 --- a/tools/assimp_qt_viewer/mainwindow.hpp +++ b/tools/assimp_qt_viewer/mainwindow.hpp @@ -65,23 +65,23 @@ private: void LogError(const QString& pMessage); /********************************************************************/ - /*********************** Overrided functions ************************/ + /*********************** Override functions ************************/ /********************************************************************/ protected: /// \fn void mousePressEvent(QMouseEvent* pEvent) override - /// Overrided function which handle mouse event "button pressed". + /// Override function which handles mouse event "button pressed". /// \param [in] pEvent - pointer to event data. void mousePressEvent(QMouseEvent* pEvent) override; /// \fn void mouseMoveEvent(QMouseEvent* pEvent) override - /// Overrided function which handle mouse event "move". + /// Override function which handles mouse event "move". /// \param [in] pEvent - pointer to event data. void mouseMoveEvent(QMouseEvent* pEvent) override; /// \fn void keyPressEvent(QKeyEvent* pEvent) override - /// Overrided function which handle key event "key pressed". + /// Override function which handles key event "key pressed". /// \param [in] pEvent - pointer to event data. void keyPressEvent(QKeyEvent* pEvent) override; diff --git a/tools/assimp_view/assimp_view.rc b/tools/assimp_view/assimp_view.rc index 13b05b18c..aebb1e2d9 100644 --- a/tools/assimp_view/assimp_view.rc +++ b/tools/assimp_view/assimp_view.rc @@ -189,7 +189,7 @@ BEGIN LTEXT "Angle limit (in degrees):",IDC_STATIC,13,10,76,8 LTEXT "The angle limit defines the maximum angle that may be between two adjacent face normals that they're smoothed together.",IDC_STATIC,13,31,253,19 CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,0,113,278,1 - LTEXT "This setting is also used during import, but it can be overriden by single model importers to match the original look of a model as closely as possible. Examples include 3DS, ASE and LWO, all of them relying on smoothing groups and their own angle limits. ",IDC_STATIC,13,51,254,33 + LTEXT "This setting is also used during import, but it can be overridden by single model importers to match the original look of a model as closely as possible. Examples include 3DS, ASE and LWO, all of them relying on smoothing groups and their own angle limits. ",IDC_STATIC,13,51,254,33 LTEXT "NOTE: New settings don't take effect immediately, use 'Smooth Normals' or 'Reload' to update the model.",IDC_STATIC,14,118,254,22 CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,0,90,277,1 END From 005b537324e588a8356804c9928f0ed3937f5643 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 10 Nov 2017 20:40:00 +0100 Subject: [PATCH 297/490] unittests: add VS-based source groups for the unittests. --- code/FIReader.hpp | 13 ++++- test/CMakeLists.txt | 139 ++++++++++++++++++++++++-------------------- 2 files changed, 86 insertions(+), 66 deletions(-) diff --git a/code/FIReader.hpp b/code/FIReader.hpp index ebc12ae31..4e9f712a9 100644 --- a/code/FIReader.hpp +++ b/code/FIReader.hpp @@ -46,11 +46,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef INCLUDED_AI_FI_READER_H #define INCLUDED_AI_FI_READER_H -#include -#include #include +#include +#include +#include #include #include +#include namespace Assimp { @@ -154,7 +156,7 @@ class IOStream; class FIReader: public irr::io::IIrrXMLReader { public: - + virtual ~FIReader(); virtual std::shared_ptr getAttributeEncodedValue(int idx) const = 0; virtual std::shared_ptr getAttributeEncodedValue(const char *name) const = 0; @@ -167,6 +169,11 @@ public: };// class IFIReader +inline +FIReader::~FIReader() { + // empty +} + }// namespace Assimp #endif // INCLUDED_AI_FI_READER_H diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cc5d3f83c..eed18a347 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -52,24 +52,55 @@ INCLUDE_DIRECTORIES( # Assimp library can be found, even if it is not installed system-wide yet. LINK_DIRECTORIES( ${Assimp_BINARY_DIR} ${AssetImporter_BINARY_DIR}/lib ) -SOURCE_GROUP( unit FILES - unit/CCompilerTest.c -) - -SET( TEST_SRCS +SET( COMMON + unit/utIOSystem.cpp + unit/utIOStreamBuffer.cpp + unit/utIssues.cpp + unit/utAnim.cpp + unit/AssimpAPITest.cpp + unit/utBatchLoader.cpp + unit/utDefaultIOStream.cpp + unit/utFastAtof.cpp + unit/utMetadata.cpp + unit/SceneDiffer.h + unit/SceneDiffer.cpp unit/UTLogStream.h unit/AbstractImportExportBase.cpp unit/TestIOSystem.h unit/TestModelFactory.h + unit/utTypes.cpp + unit/utVersion.cpp + unit/utProfiler.cpp + unit/utSharedPPData.cpp + unit/utStringUtils.cpp +) + +SET( IMPORTERS + unit/utLWSImportExport.cpp + unit/utSMDImportExport.cpp + unit/utglTFImportExport.cpp + unit/utglTF2ImportExport.cpp + unit/utHMPImportExport.cpp + unit/utIFCImportExport.cpp + unit/utFBXImporterExporter.cpp + unit/utImporter.cpp unit/ut3DImportExport.cpp unit/ut3DSImportExport.cpp unit/utACImportExport.cpp unit/utAMFImportExport.cpp unit/utASEImportExport.cpp - unit/utAnim.cpp - unit/AssimpAPITest.cpp - unit/utB3DImportExport.cpp - unit/utBatchLoader.cpp + unit/utD3MFImportExport.cpp + unit/utQ3DImportExport.cpp + unit/utSTLImportExport.cpp + unit/utXImporterExporter.cpp + unit/utX3DImportExport.cpp + unit/utDXFImporterExporter.cpp + unit/utPMXImporter.cpp + unit/utPLYImportExport.cpp + unit/utObjImportExport.cpp + unit/utObjTools.cpp + unit/utOpenGEXImportExport.cpp + unit/utSIBImporter.cpp unit/utBlenderIntermediate.cpp unit/utBlendImportAreaLight.cpp unit/utBlenderImportExport.cpp @@ -79,78 +110,60 @@ SET( TEST_SRCS unit/utColladaExportLight.cpp unit/utColladaImportExport.cpp unit/utCSMImportExport.cpp - unit/utDefaultIOStream.cpp - unit/utDXFImporterExporter.cpp - unit/utFastAtof.cpp - unit/utFBXImporterExporter.cpp - unit/utFindDegenerates.cpp - unit/utFindInvalidData.cpp - unit/utFixInfacingNormals.cpp - unit/utGenNormals.cpp - unit/utglTFImportExport.cpp - unit/utglTF2ImportExport.cpp - unit/utHMPImportExport.cpp - unit/utIFCImportExport.cpp - unit/utImporter.cpp - unit/utImproveCacheLocality.cpp - unit/utIOSystem.cpp - unit/utIOStreamBuffer.cpp - unit/utIssues.cpp - unit/utJoinVertices.cpp - unit/utLimitBoneWeights.cpp - unit/utLWSImportExport.cpp + unit/utB3DImportExport.cpp +) + +SET( MATERIAL unit/utMaterialSystem.cpp +) + +SET( MATH unit/utMatrix3x3.cpp unit/utMatrix4x4.cpp - unit/utMetadata.cpp - unit/SceneDiffer.h - unit/SceneDiffer.cpp - unit/utSIBImporter.cpp - unit/utObjImportExport.cpp - unit/utObjTools.cpp - unit/utOpenGEXImportExport.cpp - unit/utPretransformVertices.cpp - unit/utPLYImportExport.cpp - unit/utPMXImporter.cpp - unit/utRemoveComments.cpp - unit/utRemoveComponent.cpp - unit/utScenePreprocessor.cpp - unit/utSceneCombiner.cpp - unit/utSharedPPData.cpp - unit/utStringUtils.cpp - unit/utSMDImportExport.cpp - unit/utSortByPType.cpp - unit/utSplitLargeMeshes.cpp - unit/utTargetAnimation.cpp - unit/utTextureTransform.cpp - unit/utTriangulate.cpp - unit/utTypes.cpp - unit/utVertexTriangleAdjacency.cpp - unit/utVersion.cpp unit/utVector3.cpp - unit/utXImporterExporter.cpp - unit/utX3DImportExport.cpp - unit/utD3MFImportExport.cpp - unit/utQ3DImportExport.cpp - unit/utSTLImportExport.cpp - unit/utProfiler.cpp ) + SET( POST_PROCESSES + unit/utImproveCacheLocality.cpp + unit/utFixInfacingNormals.cpp + unit/utGenNormals.cpp + unit/utTriangulate.cpp + unit/utTextureTransform.cpp unit/utRemoveRedundantMaterials.cpp unit/utRemoveVCProcess.cpp unit/utScaleProcess.cpp unit/utJoinVertices.cpp + unit/utRemoveComments.cpp + unit/utRemoveComponent.cpp + unit/utVertexTriangleAdjacency.cpp + unit/utJoinVertices.cpp + unit/utSplitLargeMeshes.cpp + unit/utFindDegenerates.cpp + unit/utFindInvalidData.cpp + unit/utLimitBoneWeights.cpp + unit/utPretransformVertices.cpp + unit/utScenePreprocessor.cpp + unit/utTargetAnimation.cpp + unit/utSortByPType.cpp + unit/utSceneCombiner.cpp ) -SOURCE_GROUP( tests FILES ${TEST_SRCS} ) -SOURCE_GROUP( tests/PostProcess FILES ${POST_PROCESSES}) +SOURCE_GROUP( UnitTests\Compiler FILES unit/CCompilerTest.c ) +SOURCE_GROUP( UnitTests\\Common FILES ${COMMON} ) +SOURCE_GROUP( UnitTests\\Importers FILES ${IMPORTERS} ) +SOURCE_GROUP( UnitTests\\Material FILES ${MATERIAL} ) +SOURCE_GROUP( UnitTests\\Math FILES ${MATH} ) +SOURCE_GROUP( UnitTests\\PostProcess FILES ${POST_PROCESSES}) add_executable( unit ../contrib/gtest/src/gtest-all.cc unit/CCompilerTest.c unit/Main.cpp ../code/Version.cpp - ${TEST_SRCS} + ${COMMON} + ${IMPORTERS} + ${MATERIAL} + ${MATH} ${POST_PROCESSES} ) From d0ac06cbfdefdc7466170ae47cbf3fe642a591b8 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 11 Nov 2017 21:16:26 +0100 Subject: [PATCH 298/490] fix buggy escape sequence. --- test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index eed18a347..1c5d593de 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -148,7 +148,7 @@ SET( POST_PROCESSES unit/utSceneCombiner.cpp ) -SOURCE_GROUP( UnitTests\Compiler FILES unit/CCompilerTest.c ) +SOURCE_GROUP( UnitTests\\Compiler FILES unit/CCompilerTest.c ) SOURCE_GROUP( UnitTests\\Common FILES ${COMMON} ) SOURCE_GROUP( UnitTests\\Importers FILES ${IMPORTERS} ) SOURCE_GROUP( UnitTests\\Material FILES ${MATERIAL} ) From 24b728b3ea20d795ad3299cc7884392b514fa69b Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 12 Nov 2017 22:09:18 +0100 Subject: [PATCH 299/490] FindDegeneratives: adapt unittests and add configs --- code/FindDegenerates.cpp | 31 +++++++++-------- code/FindDegenerates.h | 59 ++++++++++++++++++++++----------- code/X3DExporter.cpp | 8 +++-- include/assimp/config.h.in | 9 +++++ include/assimp/vector3.h | 9 +++-- test/unit/utFindDegenerates.cpp | 23 +++++++------ 6 files changed, 89 insertions(+), 50 deletions(-) diff --git a/code/FindDegenerates.cpp b/code/FindDegenerates.cpp index 83dce22ba..5a4a132ef 100644 --- a/code/FindDegenerates.cpp +++ b/code/FindDegenerates.cpp @@ -56,7 +56,8 @@ using namespace Assimp; // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer FindDegeneratesProcess::FindDegeneratesProcess() -: configRemoveDegenerates (false) { +: mConfigRemoveDegenerates( false ) +, mConfigCheckAreaOfTriangle( false ){ // empty } @@ -76,7 +77,8 @@ bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const { // Setup import configuration void FindDegeneratesProcess::SetupProperties(const Importer* pImp) { // Get the current value of AI_CONFIG_PP_FD_REMOVE - configRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0)); + mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0)); + mConfigCheckAreaOfTriangle = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA) ); } // ------------------------------------------------------------------------------------------------ @@ -126,7 +128,7 @@ void FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) { mesh->mPrimitiveTypes = 0; std::vector remove_me; - if (configRemoveDegenerates) { + if (mConfigRemoveDegenerates) { remove_me.resize( mesh->mNumFaces, false ); } @@ -166,21 +168,24 @@ void FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) { first = false; } - if ( configRemoveDegenerates ) { + if ( mConfigRemoveDegenerates ) { remove_me[ a ] = true; goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby! } } } - if ( face.mNumIndices == 3 ) { - ai_real area = calculateAreaOfTriangle( face, mesh ); - if ( area < 1e-6 ) { - if ( configRemoveDegenerates ) { - remove_me[ a ] = true; - goto evil_jump_outside; - } - // todo: check for index which is corrupt. + if ( mConfigCheckAreaOfTriangle ) { + if ( face.mNumIndices == 3 ) { + ai_real area = calculateAreaOfTriangle( face, mesh ); + if ( area < 1e-6 ) { + if ( mConfigRemoveDegenerates ) { + remove_me[ a ] = true; + goto evil_jump_outside; + } + + // todo: check for index which is corrupt. + } } } } @@ -206,7 +211,7 @@ evil_jump_outside: } // If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import - if (configRemoveDegenerates && deg) { + if (mConfigRemoveDegenerates && deg) { unsigned int n = 0; for (unsigned int a = 0; a < mesh->mNumFaces; ++a) { diff --git a/code/FindDegenerates.h b/code/FindDegenerates.h index 9bd410dcd..cf03a24bc 100644 --- a/code/FindDegenerates.h +++ b/code/FindDegenerates.h @@ -54,15 +54,11 @@ namespace Assimp { // --------------------------------------------------------------------------- /** FindDegeneratesProcess: Searches a mesh for degenerated triangles. */ -class ASSIMP_API FindDegeneratesProcess : public BaseProcess -{ +class ASSIMP_API FindDegeneratesProcess : public BaseProcess { public: - FindDegeneratesProcess(); ~FindDegeneratesProcess(); -public: - // ------------------------------------------------------------------- // Check whether step is active bool IsActive( unsigned int pFlags) const; @@ -79,28 +75,53 @@ public: // Execute step on a given mesh void ExecuteOnMesh( aiMesh* mesh); + // ------------------------------------------------------------------- + /// @brief Enable the instant removal of degenerated primitives + /// @param enabled true for enabled. + void EnableInstantRemoval(bool enabled); // ------------------------------------------------------------------- - /** @brief Enable the instant removal of degenerated primitives - * @param d hm ... difficult to guess what this means, hu!? - */ - void EnableInstantRemoval(bool d) { - configRemoveDegenerates = d; - } + /// @brief Check whether instant removal is currently enabled + /// @return The instant removal state. + bool IsInstantRemoval() const; // ------------------------------------------------------------------- - /** @brief Check whether instant removal is currently enabled - * @return ... - */ - bool IsInstantRemoval() const { - return configRemoveDegenerates; - } + /// @brief Enable the area check for triangles. + /// @param enabled true for enabled. + void EnableAreaCheck( bool enabled ); + + // ------------------------------------------------------------------- + /// @brief Check whether the area check is enabled. + /// @return The area check state. + bool isAreaCheckEnabled() const; private: - //! Configuration option: remove degenerates faces immediately - bool configRemoveDegenerates; + bool mConfigRemoveDegenerates; + //! Configuration option: check for area + bool mConfigCheckAreaOfTriangle; }; + +inline +void FindDegeneratesProcess::EnableInstantRemoval(bool enabled) { + mConfigRemoveDegenerates = enabled; } +inline +bool FindDegeneratesProcess::IsInstantRemoval() const { + return mConfigRemoveDegenerates; +} + +inline +void FindDegeneratesProcess::EnableAreaCheck( bool enabled ) { + mConfigCheckAreaOfTriangle = enabled; +} + +inline +bool FindDegeneratesProcess::isAreaCheckEnabled() const { + return mConfigCheckAreaOfTriangle; +} + +} // Namespace Assimp + #endif // !! AI_FINDDEGENERATESPROCESS_H_INC diff --git a/code/X3DExporter.cpp b/code/X3DExporter.cpp index df688fd79..b560855fb 100644 --- a/code/X3DExporter.cpp +++ b/code/X3DExporter.cpp @@ -240,8 +240,12 @@ list attr_list; if((rotate_angle != 0) && (rotate_axis.Length() > 0)) attr_list.push_back({"rotation", Rotation2String(rotate_axis, rotate_angle)}); - if(!scale.Equal({1, 1, 1})) attr_list.push_back({"scale", Vector2String(scale)}); - if(translate.Length() > 0) attr_list.push_back({"translation", Vector2String(translate)}); + if(!scale.Equal({1.0,1.0,1.0})) { + attr_list.push_back({"scale", Vector2String(scale)}); + } + if(translate.Length() > 0) { + attr_list.push_back({"translation", Vector2String(translate)}); + } } // Begin node if need. diff --git a/include/assimp/config.h.in b/include/assimp/config.h.in index bafd96189..1dabae7cf 100644 --- a/include/assimp/config.h.in +++ b/include/assimp/config.h.in @@ -266,6 +266,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define AI_CONFIG_PP_FD_REMOVE \ "PP_FD_REMOVE" +// --------------------------------------------------------------------------- +/** + * @brief Configures the #aiProcess_FindDegenerates to check the area of a + * trinagle to be greates than e-6. If this is not the case the triangle will + * be removed if #AI_CONFIG_PP_FD_REMOVE is set to true. + */ +#define AI_CONFIG_PP_FD_CHECKAREA \ + "PP_FD_CHECKAREA" + // --------------------------------------------------------------------------- /** @brief Configures the #aiProcess_OptimizeGraph step to preserve nodes * matching a name in a given list. diff --git a/include/assimp/vector3.h b/include/assimp/vector3.h index 946e36cc5..641dab795 100644 --- a/include/assimp/vector3.h +++ b/include/assimp/vector3.h @@ -65,11 +65,10 @@ template class aiVector3t { public: - - aiVector3t () : x(), y(), z() {} - aiVector3t (TReal _x, TReal _y, TReal _z) : x(_x), y(_y), z(_z) {} - explicit aiVector3t (TReal _xyz) : x(_xyz), y(_xyz), z(_xyz) {} - aiVector3t (const aiVector3t& o) : x(o.x), y(o.y), z(o.z) {} + aiVector3t() : x(), y(), z() {} + aiVector3t(TReal _x, TReal _y, TReal _z) : x(_x), y(_y), z(_z) {} + explicit aiVector3t (TReal _xyz ) : x(_xyz), y(_xyz), z(_xyz) {} + aiVector3t( const aiVector3t& o ) : x(o.x), y(o.y), z(o.z) {} public: diff --git a/test/unit/utFindDegenerates.cpp b/test/unit/utFindDegenerates.cpp index 8040a83a1..dce42732c 100644 --- a/test/unit/utFindDegenerates.cpp +++ b/test/unit/utFindDegenerates.cpp @@ -58,8 +58,7 @@ protected: }; // ------------------------------------------------------------------------------------------------ -void FindDegeneratesProcessTest::SetUp() -{ +void FindDegeneratesProcessTest::SetUp() { mesh = new aiMesh(); process = new FindDegeneratesProcess(); @@ -107,16 +106,12 @@ void FindDegeneratesProcessTest::SetUp() mesh->mNumUVComponents[1] = numFaces; } -// ------------------------------------------------------------------------------------------------ -void FindDegeneratesProcessTest::TearDown() -{ +void FindDegeneratesProcessTest::TearDown() { delete mesh; delete process; } -// ------------------------------------------------------------------------------------------------ -TEST_F(FindDegeneratesProcessTest, testDegeneratesDetection) -{ +TEST_F(FindDegeneratesProcessTest, testDegeneratesDetection) { process->EnableInstantRemoval(false); process->ExecuteOnMesh(mesh); @@ -135,12 +130,18 @@ TEST_F(FindDegeneratesProcessTest, testDegeneratesDetection) mesh->mPrimitiveTypes); } -// ------------------------------------------------------------------------------------------------ -TEST_F(FindDegeneratesProcessTest, testDegeneratesRemoval) -{ +TEST_F(FindDegeneratesProcessTest, testDegeneratesRemoval) { + process->EnableAreaCheck(false); process->EnableInstantRemoval(true); process->ExecuteOnMesh(mesh); EXPECT_EQ(mesh->mNumUVComponents[1], mesh->mNumFaces); } +TEST_F(FindDegeneratesProcessTest, testDegeneratesRemovalWithAreaCheck) { + process->EnableAreaCheck(true); + process->EnableInstantRemoval(true); + process->ExecuteOnMesh(mesh); + + EXPECT_EQ(mesh->mNumUVComponents[1]-100, mesh->mNumFaces); +} From c15c96ac76f590dbea2e8adab2e1efffe99a19f1 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 14 Nov 2017 20:11:40 +0100 Subject: [PATCH 300/490] CMake: use define for D_FILE_OFFSET_BITS only for not-android systems. --- CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bc3731bbb..79db04762 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -183,8 +183,11 @@ SET(ASSIMP_LIBRARY_SUFFIX "" CACHE STRING "Suffix to append to library names") IF( UNIX ) # Ensure that we do not run into issues like http://www.tcm.phy.cam.ac.uk/sw/inodes64.html on 32 bit linux - IF ( CMAKE_SIZEOF_VOID_P EQUAL 4) # only necessary for 32-bit linux - ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 ) + IF( ${OPERATING_SYSTEM} MATCHES "Android") + ELSE() + IF ( CMAKE_SIZEOF_VOID_P EQUAL 4) # only necessary for 32-bit linux + ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 ) + ENDIF() ENDIF() # Use GNUInstallDirs for Unix predefined directories From a15bfceb7e8365823e1d85abef254fe09b197c2d Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 17 Oct 2017 18:37:40 +0300 Subject: [PATCH 301/490] Travis: Add static analysis to build --- .travis.sh | 15 +++++++++++++++ .travis.yml | 8 +++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/.travis.sh b/.travis.sh index 75f00ec60..cc981a453 100755 --- a/.travis.sh +++ b/.travis.sh @@ -43,9 +43,24 @@ if [ $ANDROID ]; then ant -v -Dmy.dir=${TRAVIS_BUILD_DIR} -f ${TRAVIS_BUILD_DIR}/port/jassimp/build.xml ndk-jni fi if [ "$TRAVIS_OS_NAME" = "linux" ]; then + if [ $ANALYZE = "ON" ] ; then + if [ "$CC" = "clang" ]; then + scan-build cmake -G "Unix Makefiles" -DBUILD_SHARED_LIBS=OFF + scan-build --status-bugs make -j2 + else + cppcheck --version + generate \ + && cppcheck --error-exitcode=1 -j2 -Iinclude -Icode code 2> cppcheck.txt + if [ -s cppcheck.txt ]; then + cat cppcheck.txt + exit 1 + fi + fi + else generate \ && make -j4 \ && sudo make install \ && sudo ldconfig \ && (cd test/unit; ../../bin/unit) + fi fi diff --git a/.travis.yml b/.travis.yml index 95e2fc164..e5477a543 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ language: cpp cache: ccache before_install: - - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get update -qq && sudo apt-get install cmake && sudo apt-get install cmake python3 && sudo apt-get install -qq freeglut3-dev libxmu-dev libxi-dev ; echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- ; fi + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get update -qq && sudo apt-get install cmake cppcheck && sudo apt-get install cmake python3 && sudo apt-get install -qq freeglut3-dev libxmu-dev libxi-dev ; echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- ; fi - 'if [ "$TRAVIS_OS_NAME" = "osx" ]; then if brew ls --versions cmake > /dev/null; then echo cmake already installed.; @@ -39,6 +39,12 @@ matrix: - os: linux compiler: gcc env: SHARED_BUILD=ON + - os: linux + compiler: gcc + env: ANALYZE=ON + - os: linux + compiler: clang + env: ANALYZE=ON - os: linux compiler: clang env: ASAN=ON From 76919e87eacf2f201ac8055ab002a7be5dc4d28b Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 17 Oct 2017 19:01:36 +0300 Subject: [PATCH 302/490] fast_atof: Silence some uninitialized variable warnings --- code/fast_atof.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/fast_atof.h b/code/fast_atof.h index be1e59221..058a7ff87 100644 --- a/code/fast_atof.h +++ b/code/fast_atof.h @@ -360,7 +360,7 @@ inline const char* fast_atoreal_move(const char* c, Real& out, bool check_comma // The same but more human. inline ai_real fast_atof(const char* c) { - ai_real ret; + ai_real ret(0.0); fast_atoreal_move(c, ret); return ret; } @@ -368,7 +368,7 @@ inline ai_real fast_atof(const char* c) inline ai_real fast_atof( const char* c, const char** cout) { - ai_real ret; + ai_real ret(0.0); *cout = fast_atoreal_move(c, ret); return ret; @@ -376,7 +376,7 @@ inline ai_real fast_atof( const char* c, const char** cout) inline ai_real fast_atof( const char** inout) { - ai_real ret; + ai_real ret(0.0); *inout = fast_atoreal_move(*inout, ret); return ret; From 59d1a1d8198fc25bb1f2a479fd5598d3cc28d629 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 17 Oct 2017 19:02:39 +0300 Subject: [PATCH 303/490] Travis: Move slower builds earlier to improve parallelization --- .travis.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index e5477a543..9265dfb38 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,15 +33,6 @@ env: matrix: include: - - os: linux - compiler: gcc - env: DISABLE_EXPORTERS=YES ENABLE_COVERALLS=ON - - os: linux - compiler: gcc - env: SHARED_BUILD=ON - - os: linux - compiler: gcc - env: ANALYZE=ON - os: linux compiler: clang env: ANALYZE=ON @@ -54,6 +45,15 @@ matrix: - os: linux compiler: clang env: SHARED_BUILD=ON + - os: linux + compiler: gcc + env: ANALYZE=ON + - os: linux + compiler: gcc + env: DISABLE_EXPORTERS=YES ENABLE_COVERALLS=ON + - os: linux + compiler: gcc + env: SHARED_BUILD=ON install: - if [ $ANDROID ]; then wget -c http://dl.google.com/android/ndk/android-ndk-${PV}-${PLATF}.tar.bz2 && tar xf android-ndk-${PV}-${PLATF}.tar.bz2 ; fi From 35907e344622d78a2073fd0214765365ba42473d Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 17 Oct 2017 19:29:54 +0300 Subject: [PATCH 304/490] Travis: Disable unit tests in scan-build config --- .travis.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.sh b/.travis.sh index cc981a453..fb42bd40d 100755 --- a/.travis.sh +++ b/.travis.sh @@ -45,7 +45,7 @@ fi if [ "$TRAVIS_OS_NAME" = "linux" ]; then if [ $ANALYZE = "ON" ] ; then if [ "$CC" = "clang" ]; then - scan-build cmake -G "Unix Makefiles" -DBUILD_SHARED_LIBS=OFF + scan-build cmake -G "Unix Makefiles" -DBUILD_SHARED_LIBS=OFF -DASSIMP_BUILD_TESTS=OFF scan-build --status-bugs make -j2 else cppcheck --version From 266e3b29a85fb56665df51775b6b08443bb6fdad Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 17 Oct 2017 19:48:08 +0300 Subject: [PATCH 305/490] RemoveRedundantMaterials: Set pointer to nullptr after deleting it --- code/RemoveRedundantMaterials.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/RemoveRedundantMaterials.cpp b/code/RemoveRedundantMaterials.cpp index 154bf63ca..8ea699f3d 100644 --- a/code/RemoveRedundantMaterials.cpp +++ b/code/RemoveRedundantMaterials.cpp @@ -145,6 +145,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene) if (!abReferenced[i]) { ++unreferencedRemoved; delete pScene->mMaterials[i]; + pScene->mMaterials[i] = nullptr; continue; } @@ -158,6 +159,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene) me = 0; aiMappingTable[i] = aiMappingTable[a]; delete pScene->mMaterials[i]; + pScene->mMaterials[i] = nullptr; break; } } From c774e864a0a7c79eb6183ddf1b95b1ed33b8bb79 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 24 Oct 2017 18:39:05 +0300 Subject: [PATCH 306/490] Remove some dead assignments --- code/Bitmap.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/Bitmap.cpp b/code/Bitmap.cpp index b1cf8a409..76994513e 100644 --- a/code/Bitmap.cpp +++ b/code/Bitmap.cpp @@ -102,7 +102,7 @@ namespace Assimp { offset += Copy(&data[offset], header.size); offset += Copy(&data[offset], header.reserved1); offset += Copy(&data[offset], header.reserved2); - offset += Copy(&data[offset], header.offset); + Copy(&data[offset], header.offset); file->Write(data, Header::header_size, 1); } @@ -122,7 +122,7 @@ namespace Assimp { offset += Copy(&data[offset], dib.x_resolution); offset += Copy(&data[offset], dib.y_resolution); offset += Copy(&data[offset], dib.nb_colors); - offset += Copy(&data[offset], dib.nb_important_colors); + Copy(&data[offset], dib.nb_important_colors); file->Write(data, DIB::dib_size, 1); } From 95f2319b41b65ff6f1947780cd328b0e05089a14 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 24 Oct 2017 19:51:12 +0300 Subject: [PATCH 307/490] ImproveCacheLocality: Add assertion to silence a static analyzer warning --- code/ImproveCacheLocality.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/ImproveCacheLocality.cpp b/code/ImproveCacheLocality.cpp index 7f0727e8b..2ac32aa84 100644 --- a/code/ImproveCacheLocality.cpp +++ b/code/ImproveCacheLocality.cpp @@ -223,6 +223,7 @@ float ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int mesh iMaxRefTris = std::max(iMaxRefTris,*piCur); } } + ai_assert(iMaxRefTris > 0); unsigned int* piCandidates = new unsigned int[iMaxRefTris*3]; unsigned int iCacheMisses = 0; From c51b92cfa32adfd670a284c9b903441f09984fd4 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 24 Oct 2017 20:05:15 +0300 Subject: [PATCH 308/490] RemoveRedundantMaterials: Add assertion to silence a static analyzer warning --- code/RemoveRedundantMaterials.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/RemoveRedundantMaterials.cpp b/code/RemoveRedundantMaterials.cpp index 8ea699f3d..c6319f892 100644 --- a/code/RemoveRedundantMaterials.cpp +++ b/code/RemoveRedundantMaterials.cpp @@ -171,6 +171,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene) // If the new material count differs from the original, // we need to rebuild the material list and remap mesh material indexes. if (iNewNum != pScene->mNumMaterials) { + ai_assert(iNewNum > 0); aiMaterial** ppcMaterials = new aiMaterial*[iNewNum]; ::memset(ppcMaterials,0,sizeof(void*)*iNewNum); for (unsigned int p = 0; p < pScene->mNumMaterials;++p) From a7fccf8f3352bc3e20689a46d4a8d0ce30384cb8 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 7 Nov 2017 18:18:01 +0200 Subject: [PATCH 309/490] OptimizeGraph: Fix possible null pointer dereference --- code/OptimizeGraph.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/OptimizeGraph.cpp b/code/OptimizeGraph.cpp index 04971af5e..0e510e5ea 100644 --- a/code/OptimizeGraph.cpp +++ b/code/OptimizeGraph.cpp @@ -233,11 +233,13 @@ void OptimizeGraphProcess::CollectNewChildren(aiNode* nd, std::list& no nd->mNumChildren = static_cast(child_nodes.size()); + if (nd->mChildren) { aiNode** tmp = nd->mChildren; for (std::list::iterator it = child_nodes.begin(); it != child_nodes.end(); ++it) { aiNode* node = *tmp++ = *it; node->mParent = nd; } + } nodes_out += static_cast(child_nodes.size()); } From bd0d47c5fc85dd19aec48a2d993c43fd0f5b608f Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 7 Nov 2017 18:18:34 +0200 Subject: [PATCH 310/490] Whitespace --- code/OptimizeGraph.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/code/OptimizeGraph.cpp b/code/OptimizeGraph.cpp index 0e510e5ea..11a6fcf65 100644 --- a/code/OptimizeGraph.cpp +++ b/code/OptimizeGraph.cpp @@ -234,11 +234,11 @@ void OptimizeGraphProcess::CollectNewChildren(aiNode* nd, std::list& no nd->mNumChildren = static_cast(child_nodes.size()); if (nd->mChildren) { - aiNode** tmp = nd->mChildren; - for (std::list::iterator it = child_nodes.begin(); it != child_nodes.end(); ++it) { - aiNode* node = *tmp++ = *it; - node->mParent = nd; - } + aiNode** tmp = nd->mChildren; + for (std::list::iterator it = child_nodes.begin(); it != child_nodes.end(); ++it) { + aiNode* node = *tmp++ = *it; + node->mParent = nd; + } } nodes_out += static_cast(child_nodes.size()); From 2c7770eed549bf3137c89c32e0f6012fa95b0c1e Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 7 Nov 2017 18:38:24 +0200 Subject: [PATCH 311/490] AMFImporter: Add a block --- code/AMFImporter_Postprocess.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/AMFImporter_Postprocess.cpp b/code/AMFImporter_Postprocess.cpp index 0e639026c..3737116f2 100644 --- a/code/AMFImporter_Postprocess.cpp +++ b/code/AMFImporter_Postprocess.cpp @@ -281,8 +281,9 @@ size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string& { if(!pID.empty()) { - for(size_t idx_target = pOffset, idx_src = 0; idx_target < tex_size; idx_target += pStep, idx_src++) + for(size_t idx_target = pOffset, idx_src = 0; idx_target < tex_size; idx_target += pStep, idx_src++) { converted_texture.Data[idx_target] = src_texture[pSrcTexNum]->Data.at(idx_src); + } } };// auto CopyTextureData = [&](const size_t pOffset, const size_t pStep, const uint8_t pSrcTexNum) -> void From 856d402b59725b99b9af21e3831c8cdb33383989 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 7 Nov 2017 18:44:16 +0200 Subject: [PATCH 312/490] AMFImporter: Add assertion to silence a static analyzer warning --- code/AMFImporter_Postprocess.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/AMFImporter_Postprocess.cpp b/code/AMFImporter_Postprocess.cpp index 3737116f2..991eb97e7 100644 --- a/code/AMFImporter_Postprocess.cpp +++ b/code/AMFImporter_Postprocess.cpp @@ -282,7 +282,9 @@ size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string& if(!pID.empty()) { for(size_t idx_target = pOffset, idx_src = 0; idx_target < tex_size; idx_target += pStep, idx_src++) { - converted_texture.Data[idx_target] = src_texture[pSrcTexNum]->Data.at(idx_src); + CAMFImporter_NodeElement_Texture* tex = src_texture[pSrcTexNum]; + ai_assert(tex); + converted_texture.Data[idx_target] = tex->Data.at(idx_src); } } };// auto CopyTextureData = [&](const size_t pOffset, const size_t pStep, const uint8_t pSrcTexNum) -> void From 4c9f1691091b88a61e60f72b4365cf72ff4ea333 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 7 Nov 2017 18:45:02 +0200 Subject: [PATCH 313/490] ASE: Add assertion to silence a static analyzer warning --- code/ASELoader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/ASELoader.cpp b/code/ASELoader.cpp index 16b3c4ad9..65935f48f 100644 --- a/code/ASELoader.cpp +++ b/code/ASELoader.cpp @@ -1021,6 +1021,7 @@ void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, std::vector& avOutMesh // convert bones, if existing if (!mesh.mBones.empty()) { + ai_assert(avOutputBones); // check whether there is a vertex weight for this vertex index if (iIndex2 < mesh.mBoneVertices.size()) { From 437816fc33d4e1ef60d5470c467d2300d2d67827 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 7 Nov 2017 19:05:01 +0200 Subject: [PATCH 314/490] AssbinExporter: Add assertion to silence a static analyzer warning --- code/AssbinExporter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/AssbinExporter.cpp b/code/AssbinExporter.cpp index 6bee6d6d1..bcac2e08f 100644 --- a/code/AssbinExporter.cpp +++ b/code/AssbinExporter.cpp @@ -171,6 +171,7 @@ inline size_t Write(IOStream * stream, const aiQuaternion& v) t += Write(stream,v.x); t += Write(stream,v.y); t += Write(stream,v.z); + ai_assert(t == 16); return 16; } From a276a027263f8cd6eefc97d524bdab952ec04d6d Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 7 Nov 2017 19:11:08 +0200 Subject: [PATCH 315/490] IRRLoader: Fix confusing boolean casting --- code/IRRLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/IRRLoader.cpp b/code/IRRLoader.cpp index 5113ab70d..c29d5b072 100644 --- a/code/IRRLoader.cpp +++ b/code/IRRLoader.cpp @@ -394,7 +394,7 @@ void IRRImporter::ComputeAnimations(Node* root, aiNode* real, std::vector Date: Tue, 7 Nov 2017 19:12:12 +0200 Subject: [PATCH 316/490] LWO: Reduce scope of a variable --- code/LWOAnimation.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/code/LWOAnimation.cpp b/code/LWOAnimation.cpp index 61c696453..4b5338f7a 100644 --- a/code/LWOAnimation.cpp +++ b/code/LWOAnimation.cpp @@ -446,8 +446,6 @@ void AnimResolver::GetKeys(std::vector& out, // Iterate through all three arrays at once - it's tricky, but // rather interesting to implement. - double lasttime = std::min(envl_x->keys[0].time,std::min(envl_y->keys[0].time,envl_z->keys[0].time)); - cur_x = envl_x->keys.begin(); cur_y = envl_y->keys.begin(); cur_z = envl_z->keys.begin(); @@ -503,7 +501,7 @@ void AnimResolver::GetKeys(std::vector& out, InterpolateTrack(out,fill,(end_y ? (*cur_x) : (*cur_y)).time); } } - lasttime = fill.mTime; + double lasttime = fill.mTime; out.push_back(fill); if (lasttime >= (*cur_x).time) { From 2b93a210c9a0142599a19d61d547f47e93c76278 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 7 Nov 2017 19:12:44 +0200 Subject: [PATCH 317/490] NFF: Reduce scope of a variable --- code/NFFLoader.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/code/NFFLoader.cpp b/code/NFFLoader.cpp index fa005ede4..b6dd2fb33 100644 --- a/code/NFFLoader.cpp +++ b/code/NFFLoader.cpp @@ -243,8 +243,6 @@ void NFFImporter::InternReadFile( const std::string& pFile, if( !file.get()) throw DeadlyImportError( "Failed to open NFF file " + pFile + "."); - unsigned int m = (unsigned int)file->FileSize(); - // allocate storage and copy the contents of the file to a memory buffer // (terminate it with zero) std::vector mBuffer2; @@ -469,7 +467,7 @@ void NFFImporter::InternReadFile( const std::string& pFile, for (unsigned int a = 0; a < numIdx;++a) { SkipSpaces(sz,&sz); - m = ::strtoul10(sz,&sz); + unsigned int m = ::strtoul10(sz,&sz); if (m >= (unsigned int)tempPositions.size()) { DefaultLogger::get()->error("NFF2: Vertex index overflow"); @@ -635,7 +633,7 @@ void NFFImporter::InternReadFile( const std::string& pFile, for (std::vector::const_iterator it = tempIdx.begin(), end = tempIdx.end(); it != end;++it) { - m = *it; + unsigned int m = *it; // copy colors -vertex color specifications override polygon color specifications if (hasColor) @@ -735,7 +733,7 @@ void NFFImporter::InternReadFile( const std::string& pFile, sz = &line[1];out = currentMesh; } SkipSpaces(sz,&sz); - m = strtoul10(sz); + unsigned int m = strtoul10(sz); // ---- flip the face order out->vertices.resize(out->vertices.size()+m); @@ -1128,7 +1126,8 @@ void NFFImporter::InternReadFile( const std::string& pFile, if (!pScene->mNumMeshes)throw DeadlyImportError("NFF: No meshes loaded"); pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = pScene->mNumMeshes]; - for (it = meshes.begin(), m = 0; it != end;++it) + unsigned int m = 0; + for (it = meshes.begin(); it != end;++it) { if ((*it).faces.empty())continue; From d24e0d44b28b05f18498928dc0abb65f24fbdb09 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 7 Nov 2017 19:15:06 +0200 Subject: [PATCH 318/490] Raw: Fix misleading indentation warning --- code/RawLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/RawLoader.cpp b/code/RawLoader.cpp index e14b5140d..2b76455d7 100644 --- a/code/RawLoader.cpp +++ b/code/RawLoader.cpp @@ -260,7 +260,7 @@ void RAWImporter::InternReadFile( const std::string& pFile, node = *cc = new aiNode(); node->mParent = pScene->mRootNode; } - else node = *cc;++cc; + else node = *cc; node->mName.Set(outGroup.name); // add all meshes From 3f299b2a2bf56d75bb3336bda19dab445be66fcd Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 7 Nov 2017 19:32:33 +0200 Subject: [PATCH 319/490] NFF: Split up some complicated assignments --- code/NFFLoader.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/code/NFFLoader.cpp b/code/NFFLoader.cpp index b6dd2fb33..396fb28bf 100644 --- a/code/NFFLoader.cpp +++ b/code/NFFLoader.cpp @@ -1079,7 +1079,8 @@ void NFFImporter::InternReadFile( const std::string& pFile, // generate the camera if (hasCam) { - aiNode* nd = *ppcChildren = new aiNode(); + aiNode* nd = new aiNode(); + *ppcChildren = nd; nd->mName.Set(""); nd->mParent = root; @@ -1109,7 +1110,8 @@ void NFFImporter::InternReadFile( const std::string& pFile, { const Light& l = lights[i]; - aiNode* nd = *ppcChildren = new aiNode(); + aiNode* nd = new aiNode(); + *ppcChildren = nd; nd->mParent = root; nd->mName.length = ::ai_snprintf(nd->mName.data,1024,"",i); From f90019bc1ee8a6283dff09dc02977aff42fee1bb Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 7 Nov 2017 19:38:31 +0200 Subject: [PATCH 320/490] NFF: Add assertions to silence static analyzer warnings --- code/NFFLoader.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/NFFLoader.cpp b/code/NFFLoader.cpp index 396fb28bf..a51c500f1 100644 --- a/code/NFFLoader.cpp +++ b/code/NFFLoader.cpp @@ -1079,6 +1079,7 @@ void NFFImporter::InternReadFile( const std::string& pFile, // generate the camera if (hasCam) { + ai_assert(ppcChildren); aiNode* nd = new aiNode(); *ppcChildren = nd; nd->mName.Set(""); @@ -1104,6 +1105,7 @@ void NFFImporter::InternReadFile( const std::string& pFile, // generate light sources if (!lights.empty()) { + ai_assert(ppcChildren); pScene->mNumLights = (unsigned int)lights.size(); pScene->mLights = new aiLight*[pScene->mNumLights]; for (unsigned int i = 0; i < pScene->mNumLights;++i,++ppcChildren) From f470b8466fb52b13ff3e11c917072fc44d271ac4 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 14 Nov 2017 18:42:04 +0200 Subject: [PATCH 321/490] GLTF2: Fix signed/unsigned warning --- code/glTF2Asset.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 37a8fc662..f37d592c1 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -204,7 +204,7 @@ unsigned int LazyDict::Remove(const char* id) throw DeadlyExportError("GLTF: Object with id \"" + std::string(id) + "\" is not found"); } - const int index = it->second; + const unsigned int index = it->second; mAsset.mUsedIds[id] = false; mObjsById.erase(id); From 97843f19d2301b489e4bd65eb7fa6c2f7ca4c5dc Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 14 Nov 2017 18:46:27 +0200 Subject: [PATCH 322/490] OpenGEX: Add assertion to silence a static analyzer warning --- code/OpenGEXImporter.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 52e9ce501..025356c1d 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -652,6 +652,8 @@ static void setMatrix( aiNode *node, DataArrayList *transformData ) { i++; } + ai_assert(i == 16); + node->mTransformation.a1 = m[ 0 ]; node->mTransformation.a2 = m[ 4 ]; node->mTransformation.a3 = m[ 8 ]; From b49a4e133884ba2ed73cd16cd07250b4d875dd70 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 14 Nov 2017 18:48:07 +0200 Subject: [PATCH 323/490] PLY: Remove dead assignment and reduce scope of a variable --- code/PlyParser.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code/PlyParser.cpp b/code/PlyParser.cpp index 65de665ab..6321821d2 100644 --- a/code/PlyParser.cpp +++ b/code/PlyParser.cpp @@ -671,7 +671,6 @@ bool PLY::ElementInstanceList::ParseInstanceList( PLYImporter* loader) { ai_assert(NULL != pcElement); - const char* pCur = (const char*)&buffer[0]; // parse all elements if (EEST_INVALID == pcElement->eSemantic || pcElement->alProperties.empty()) @@ -683,11 +682,11 @@ bool PLY::ElementInstanceList::ParseInstanceList( PLY::DOM::SkipComments(buffer); PLY::DOM::SkipLine(buffer); streamBuffer.getNextLine(buffer); - pCur = (buffer.empty()) ? NULL : (const char*)&buffer[0]; } } else { + const char* pCur = (const char*)&buffer[0]; // be sure to have enough storage for (unsigned int i = 0; i < pcElement->NumOccur; ++i) { From ef0af40f9005dae1017a5a44131e15a7c3322c1f Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 14 Nov 2017 18:53:08 +0200 Subject: [PATCH 324/490] IFC: Remove dead code --- code/IFCOpenings.cpp | 41 +---------------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/code/IFCOpenings.cpp b/code/IFCOpenings.cpp index 45e0d1b90..4d57cd473 100644 --- a/code/IFCOpenings.cpp +++ b/code/IFCOpenings.cpp @@ -1499,7 +1499,7 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: IfcVector3 wall_extrusion; - bool do_connections = false, first = true; + bool first = true; try { @@ -1527,7 +1527,6 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: if (first) { first = false; if (dot > 0.f) { - do_connections = true; wall_extrusion = t.extrusionDir; if (is_extruded_side) { wall_extrusion = - wall_extrusion; @@ -1607,44 +1606,6 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: old_verts.swap(curmesh.verts); old_vertcnt.swap(curmesh.vertcnt); - - // add connection geometry to close the adjacent 'holes' for the openings - // this should only be done from one side of the wall or the polygons - // would be emitted twice. - if (false && do_connections) { - - std::vector tmpvec; - for(ClipperLib::Polygon& opening : holes_union) { - - ai_assert(ClipperLib::Orientation(opening)); - - tmpvec.clear(); - - for(ClipperLib::IntPoint& point : opening) { - - tmpvec.push_back( minv * IfcVector3( - vmin.x + from_int64(point.X) * vmax.x, - vmin.y + from_int64(point.Y) * vmax.y, - coord)); - } - - for(size_t i = 0, size = tmpvec.size(); i < size; ++i) { - const size_t next = (i+1)%size; - - curmesh.vertcnt.push_back(4); - - const IfcVector3& in_world = tmpvec[i]; - const IfcVector3& next_world = tmpvec[next]; - - // Assumptions: no 'partial' openings, wall thickness roughly the same across the wall - curmesh.verts.push_back(in_world); - curmesh.verts.push_back(in_world+wall_extrusion); - curmesh.verts.push_back(next_world+wall_extrusion); - curmesh.verts.push_back(next_world); - } - } - } - std::vector< std::vector > contours; for(ClipperLib::ExPolygon& clip : clipped) { From 583d3f88b8cdb1719c47cb2bf84ed1074a659497 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 14 Nov 2017 19:05:53 +0200 Subject: [PATCH 325/490] FBX: Remove dead assignment --- code/FBXConverter.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index a1e3ae07c..a5e44a607 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -566,7 +566,6 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa if ( !name_carrier ) { nodes_chain.push_back( new aiNode( original_name ) ); - name_carrier = nodes_chain.back(); } //setup metadata on newest node From e47bf932e8b8a7780e996d9800a5414c3d3cbd17 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 14 Nov 2017 19:08:41 +0200 Subject: [PATCH 326/490] SIBImporter: Add assertions to silence static analyzer warnings --- code/SIBImporter.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/SIBImporter.cpp b/code/SIBImporter.cpp index e85c9bdcb..2127e2e0c 100644 --- a/code/SIBImporter.cpp +++ b/code/SIBImporter.cpp @@ -902,6 +902,7 @@ void SIBImporter::InternReadFile(const std::string& pFile, // Add nodes for each object. for (size_t n=0;nmChildren); SIBObject& obj = sib.objs[n]; aiNode* node = new aiNode; root->mChildren[childIdx++] = node; @@ -926,6 +927,7 @@ void SIBImporter::InternReadFile(const std::string& pFile, // (no transformation as the light is already in world space) for (size_t n=0;nmChildren); aiLight* light = sib.lights[n]; if ( nullptr != light ) { aiNode* node = new aiNode; From 66c18cc4062ff68ef1fc7dbe1deb333c2be1f0e2 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 14 Nov 2017 19:13:14 +0200 Subject: [PATCH 327/490] TerragenLoader: Remove unused variable --- code/TerragenLoader.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/code/TerragenLoader.cpp b/code/TerragenLoader.cpp index ecf33aae9..0126229ac 100644 --- a/code/TerragenLoader.cpp +++ b/code/TerragenLoader.cpp @@ -141,9 +141,6 @@ void TerragenImporter::InternReadFile( const std::string& pFile, throw DeadlyImportError( "TER: Magic string \'TERRAIN\' not found" ); unsigned int x = 0,y = 0,mode = 0; - float rad = 6370.f; - (void)rad; - aiNode* root = pScene->mRootNode = new aiNode(); root->mName.Set(""); @@ -187,7 +184,7 @@ void TerragenImporter::InternReadFile( const std::string& pFile, // mapping == 1: earth radius else if (!::strncmp(head,AI_TERR_CHUNK_CRAD,4)) { - rad = reader.GetF4(); + reader.GetF4(); } // mapping mode else if (!::strncmp(head,AI_TERR_CHUNK_CRVM,4)) From f475803f931f45add900090d4b8f9d81b1a9c85e Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 14 Nov 2017 19:24:34 +0200 Subject: [PATCH 328/490] X3DImporter: Add assertions to silence static analyzer warnings --- code/X3DImporter_Postprocess.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/code/X3DImporter_Postprocess.cpp b/code/X3DImporter_Postprocess.cpp index 0e3f3a8ae..390ef8995 100644 --- a/code/X3DImporter_Postprocess.cpp +++ b/code/X3DImporter_Postprocess.cpp @@ -48,6 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "X3DImporter.hpp" // Header files, Assimp. +#include #include "StandardShapes.h" #include "StringUtils.h" @@ -357,6 +358,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle // copy additional information from children for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++) { + ai_assert(*pMesh); if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex); else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) @@ -389,6 +391,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle // copy additional information from children for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++) { + ai_assert(*pMesh); if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex); else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) @@ -446,6 +449,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle // copy additional information from children for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++) { + ai_assert(*pMesh); if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, true); else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) @@ -475,6 +479,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle // copy additional information from children for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++) { + ai_assert(*pMesh); if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, true); else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) @@ -550,6 +555,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle // copy additional information from children for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++) { + ai_assert(*pMesh); if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex); else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) @@ -584,6 +590,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle // copy additional information from children for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++) { + ai_assert(*pMesh); if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex); else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) From 9dadec77364816acfdabaa382a24bbc4b04d5ffa Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 14 Nov 2017 19:46:35 +0200 Subject: [PATCH 329/490] assimp_cmd: Add assertion to silence a static analyzer warning --- tools/assimp_cmd/WriteDumb.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/assimp_cmd/WriteDumb.cpp b/tools/assimp_cmd/WriteDumb.cpp index 3fe8b2c77..69d4efcc5 100644 --- a/tools/assimp_cmd/WriteDumb.cpp +++ b/tools/assimp_cmd/WriteDumb.cpp @@ -198,6 +198,7 @@ inline uint32_t Write(const aiQuaternion& v) t += Write(v.x); t += Write(v.y); t += Write(v.z); + ai_assert(t == 16); return 16; } From eb5f47f5c5b96af42c1633b371909b5c37ab6e90 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 15 Nov 2017 11:58:13 +0200 Subject: [PATCH 330/490] OpenDDLParser: Fix potential memory leak --- contrib/openddlparser/code/OpenDDLParser.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp index dab976fad..c42fc9d57 100644 --- a/contrib/openddlparser/code/OpenDDLParser.cpp +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -27,6 +27,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #ifdef _WIN32 @@ -275,12 +276,12 @@ char *OpenDDLParser::parseHeader( char *in, char *end ) { } delete id; - Name *name(ddl_nullptr); - in = OpenDDLParser::parseName(in, end, &name); + Name *name_(ddl_nullptr); + in = OpenDDLParser::parseName(in, end, &name_); + std::unique_ptr name(name_); if( ddl_nullptr != name && ddl_nullptr != node ) { const std::string nodeName( name->m_id->m_buffer ); node->setName( nodeName ); - delete name; } From 635a515e69af97793eec9f1b45a2e397cc3e2cfd Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 15 Nov 2017 12:06:21 +0200 Subject: [PATCH 331/490] OpenDDLParser: Fix another potential memory leak --- contrib/openddlparser/code/OpenDDLParser.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp index c42fc9d57..6e0e2322e 100644 --- a/contrib/openddlparser/code/OpenDDLParser.cpp +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -285,13 +285,15 @@ char *OpenDDLParser::parseHeader( char *in, char *end ) { } - Property *first(ddl_nullptr); + std::unique_ptr first; in = lookForNextToken(in, end); if (*in == Grammar::OpenPropertyToken[0]) { in++; - Property *prop(ddl_nullptr), *prev(ddl_nullptr); + std::unique_ptr prop, prev; while (*in != Grammar::ClosePropertyToken[0] && in != end) { - in = OpenDDLParser::parseProperty(in, end, &prop); + Property *prop_(ddl_nullptr); + in = OpenDDLParser::parseProperty(in, end, &prop_); + prop.reset(prop_); in = lookForNextToken(in, end); if (*in != Grammar::CommaSeparator[0] && *in != Grammar::ClosePropertyToken[0]) { @@ -301,20 +303,20 @@ char *OpenDDLParser::parseHeader( char *in, char *end ) { if (ddl_nullptr != prop && *in != Grammar::CommaSeparator[0]) { if (ddl_nullptr == first) { - first = prop; + first = std::move(prop); } if (ddl_nullptr != prev) { - prev->m_next = prop; + prev->m_next = prop.release(); } - prev = prop; + prev = std::move(prop); } } ++in; } // set the properties - if (ddl_nullptr != first && ddl_nullptr != node) { - node->setProperties(first); + if (first && ddl_nullptr != node) { + node->setProperties(first.release()); } } From 58d5d04e82327d3884a2cdc2a9f45bc18beb9a46 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 15 Nov 2017 12:06:30 +0200 Subject: [PATCH 332/490] OpenDDLParser: Remove dead assignment --- contrib/openddlparser/code/OpenDDLParser.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp index 6e0e2322e..caa281364 100644 --- a/contrib/openddlparser/code/OpenDDLParser.cpp +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -342,7 +342,6 @@ char *OpenDDLParser::parseStructure( char *in, char *end ) { } else { ++in; logInvalidTokenError( in, std::string( Grammar::OpenBracketToken ), m_logCallback ); - error = true; return ddl_nullptr; } in = lookForNextToken( in, end ); From be1d346c28c1ca0e7db109368285f90303cd00d9 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 15 Nov 2017 12:12:06 +0200 Subject: [PATCH 333/490] Open3DGC: Add assertions to silence static analyzer warnings --- contrib/Open3DGC/o3dgcArithmeticCodec.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/Open3DGC/o3dgcArithmeticCodec.cpp b/contrib/Open3DGC/o3dgcArithmeticCodec.cpp index 1d160ba95..3597ec39a 100644 --- a/contrib/Open3DGC/o3dgcArithmeticCodec.cpp +++ b/contrib/Open3DGC/o3dgcArithmeticCodec.cpp @@ -820,6 +820,7 @@ namespace o3dgc for (unsigned n = 0; n < data_symbols; n++) total_count += (symbol_count[n] = (symbol_count[n] + 1) >> 1); } + assert(total_count > 0); // compute cumulative distribution, decoder table unsigned k, sum = 0, s = 0; unsigned scale = 0x80000000U / total_count; @@ -830,6 +831,7 @@ namespace o3dgc sum += symbol_count[k]; } else { + assert(decoder_table); for (k = 0; k < data_symbols; k++) { distribution[k] = (scale * sum) >> (31 - DM__LengthShift); sum += symbol_count[k]; From ef91211231c0ad730682fc3f553e4462356f0660 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 15 Nov 2017 12:14:56 +0200 Subject: [PATCH 334/490] OpenDDLExport: Remove dead variable --- contrib/openddlparser/code/OpenDDLExport.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contrib/openddlparser/code/OpenDDLExport.cpp b/contrib/openddlparser/code/OpenDDLExport.cpp index a85bc5676..bb6d5532f 100644 --- a/contrib/openddlparser/code/OpenDDLExport.cpp +++ b/contrib/openddlparser/code/OpenDDLExport.cpp @@ -135,10 +135,9 @@ bool OpenDDLExport::writeToStream( const std::string &statement ) { } bool OpenDDLExport::writeNode( DDLNode *node, std::string &statement ) { - bool success( true ); writeNodeHeader( node, statement ); if (node->hasProperties()) { - success |= writeProperties( node, statement ); + writeProperties( node, statement ); } writeLineEnd( statement ); From fc59f190ae5e6ced9242cb711633f4709a315c02 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 15 Nov 2017 12:15:53 +0200 Subject: [PATCH 335/490] OpenDDLExport: Reduce scope of a variable --- contrib/openddlparser/code/OpenDDLExport.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contrib/openddlparser/code/OpenDDLExport.cpp b/contrib/openddlparser/code/OpenDDLExport.cpp index bb6d5532f..e45fb041a 100644 --- a/contrib/openddlparser/code/OpenDDLExport.cpp +++ b/contrib/openddlparser/code/OpenDDLExport.cpp @@ -359,11 +359,10 @@ bool OpenDDLExport::writeValueArray( DataArrayList *al, std::string &statement ) } DataArrayList *nextDataArrayList = al ; - Value *nextValue( nextDataArrayList->m_dataList ); while (ddl_nullptr != nextDataArrayList) { if (ddl_nullptr != nextDataArrayList) { statement += "{ "; - nextValue = nextDataArrayList->m_dataList; + Value *nextValue( nextDataArrayList->m_dataList ); size_t idx( 0 ); while (ddl_nullptr != nextValue) { if (idx > 0) { From 11fdaa31bcadb05515bc9205eb5352f6f35e5a89 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 15 Nov 2017 12:18:39 +0200 Subject: [PATCH 336/490] clipper: Add assertion to silence a static analyzer warning --- contrib/clipper/clipper.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/clipper/clipper.cpp b/contrib/clipper/clipper.cpp index 7d2af7d3a..d10cb1e5c 100644 --- a/contrib/clipper/clipper.cpp +++ b/contrib/clipper/clipper.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -2365,6 +2366,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) //ok, so far it looks like we're still in range of the horizontal edge if ( e->xcurr == horzEdge->xtop && !eMaxPair ) { + assert(horzEdge->nextInLML); if (SlopesEqual(*e, *horzEdge->nextInLML, m_UseFullRange)) { //if output polygons share an edge, they'll need joining later ... From 0bc259fd9541713e46810d0e0b1fe5ec79b6d3e6 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 15 Nov 2017 12:30:18 +0200 Subject: [PATCH 337/490] unzip: Fix possibly uninitialized variables --- contrib/unzip/unzip.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/unzip/unzip.c b/contrib/unzip/unzip.c index 085d79a02..51a3d8b4e 100644 --- a/contrib/unzip/unzip.c +++ b/contrib/unzip/unzip.c @@ -204,7 +204,7 @@ local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX) uLong *pX; { uLong x ; - int i; + int i = 0; int err; err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); @@ -232,7 +232,7 @@ local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX) uLong *pX; { uLong x ; - int i; + int i = 0; int err; err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); From 9b887153610e6aa8ed0f50eab50aba9673968395 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 15 Nov 2017 12:32:24 +0200 Subject: [PATCH 338/490] unzip: Bail on bad compression method --- contrib/unzip/unzip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/unzip/unzip.c b/contrib/unzip/unzip.c index 51a3d8b4e..0fac0b29d 100644 --- a/contrib/unzip/unzip.c +++ b/contrib/unzip/unzip.c @@ -1129,7 +1129,7 @@ extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) if ((s->cur_file_info.compression_method!=0) && (s->cur_file_info.compression_method!=Z_DEFLATED)) - err=UNZ_BADZIPFILE; + return UNZ_BADZIPFILE; pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; pfile_in_zip_read_info->crc32=0; From c248ae37974ed9eb9da0c780ddb54b2022b64e14 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 15 Nov 2017 12:33:34 +0200 Subject: [PATCH 339/490] unzip: Remove dead assignments --- contrib/unzip/unzip.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/contrib/unzip/unzip.c b/contrib/unzip/unzip.c index 0fac0b29d..2ab621a6f 100644 --- a/contrib/unzip/unzip.c +++ b/contrib/unzip/unzip.c @@ -733,11 +733,9 @@ local int unzlocal_GetCurrentFileInfoInternal (file, if ((file_info.size_file_comment>0) && (commentBufferSize>0)) if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; - lSeek+=file_info.size_file_comment - uSizeRead; } else { - lSeek+=file_info.size_file_comment; } if ((err==UNZ_OK) && (pfile_info!=NULL)) From 76de3e08283afb1c16f1b6cb1af87eeb10aa1edf Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 15 Nov 2017 12:35:06 +0200 Subject: [PATCH 340/490] clipper: Add assertion to silence a static analyzer warning --- contrib/clipper/clipper.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/clipper/clipper.cpp b/contrib/clipper/clipper.cpp index d10cb1e5c..074f22b21 100644 --- a/contrib/clipper/clipper.cpp +++ b/contrib/clipper/clipper.cpp @@ -2431,6 +2431,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) if ( horzEdge->outIdx >= 0 ) IntersectEdges( horzEdge, eMaxPair, IntPoint(horzEdge->xtop, horzEdge->ycurr), ipBoth); + assert(eMaxPair); if (eMaxPair->outIdx >= 0) throw clipperException("ProcessHorizontal error"); DeleteFromAEL(eMaxPair); DeleteFromAEL(horzEdge); From 983e52e308476a115fd410997839b6b0b510611f Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 15 Nov 2017 12:45:27 +0200 Subject: [PATCH 341/490] unzip: Remove dead assignment --- contrib/unzip/unzip.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contrib/unzip/unzip.c b/contrib/unzip/unzip.c index 2ab621a6f..e8b62e763 100644 --- a/contrib/unzip/unzip.c +++ b/contrib/unzip/unzip.c @@ -725,9 +725,7 @@ local int unzlocal_GetCurrentFileInfoInternal (file, if (lSeek!=0) { - if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) - lSeek=0; - else + if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)!=0) err=UNZ_ERRNO; } if ((file_info.size_file_comment>0) && (commentBufferSize>0)) From 7db10022e915905accdbd2de3ba47ea41a556b5f Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 15 Nov 2017 21:26:25 +0100 Subject: [PATCH 342/490] closes https://github.com/assimp/assimp/issues/1513: fix assimp for cross compile for android --- code/DefaultIOSystem.cpp | 1 - code/glTF2Asset.h | 4 +- code/glTF2Asset.inl | 4 +- code/glTF2Exporter.cpp | 2 +- code/glTFAsset.inl | 2 +- contrib/android-cmake/AndroidNdkGdb.cmake | 96 + contrib/android-cmake/AndroidNdkModules.cmake | 58 + contrib/android-cmake/README.md | 240 +++ contrib/android-cmake/android.toolchain.cmake | 1693 +++++++++++++++++ contrib/android-cmake/ndk_links.md | 211 ++ contrib/zlib/zconf.h.included | 2 + scripts/android_crosscompile/make_android.bat | 28 + 12 files changed, 2335 insertions(+), 6 deletions(-) create mode 100644 contrib/android-cmake/AndroidNdkGdb.cmake create mode 100644 contrib/android-cmake/AndroidNdkModules.cmake create mode 100644 contrib/android-cmake/README.md create mode 100644 contrib/android-cmake/android.toolchain.cmake create mode 100644 contrib/android-cmake/ndk_links.md create mode 100644 scripts/android_crosscompile/make_android.bat diff --git a/code/DefaultIOSystem.cpp b/code/DefaultIOSystem.cpp index ca4ae4564..8687b0059 100644 --- a/code/DefaultIOSystem.cpp +++ b/code/DefaultIOSystem.cpp @@ -49,7 +49,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include - #ifdef __unix__ #include #include diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 7ebc5ba95..877b59ba3 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -88,6 +88,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # endif #endif +#include "StringUtils.h" + namespace glTF2 { #ifdef ASSIMP_API @@ -239,7 +241,7 @@ namespace glTF2 case ComponentType_UNSIGNED_BYTE: return 1; default: - throw DeadlyImportError("GLTF: Unsupported Component Type " + std::to_string(t)); + throw DeadlyImportError("GLTF: Unsupported Component Type " + to_string(t)); } } diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 37a8fc662..d0ad129e3 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -258,11 +258,11 @@ Ref LazyDict::Retrieve(unsigned int i) Value &obj = (*mDict)[i]; if (!obj.IsObject()) { - throw DeadlyImportError("GLTF: Object at index \"" + std::to_string(i) + "\" is not a JSON object"); + throw DeadlyImportError("GLTF: Object at index \"" + to_string(i) + "\" is not a JSON object"); } T* inst = new T(); - inst->id = std::string(mDictId) + "_" + std::to_string(i); + inst->id = std::string(mDictId) + "_" + to_string(i); inst->oIndex = i; ReadMember(obj, "name", inst->name); inst->Read(obj, mAsset); diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index a7a640b5d..ed6814b7f 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -402,7 +402,7 @@ void glTF2Exporter::ExportMaterials() for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) { const aiMaterial* mat = mScene->mMaterials[i]; - std::string id = "material_" + std::to_string(i); + std::string id = "material_" + to_string(i); Ref m = mAsset->materials.Create(id); diff --git a/code/glTFAsset.inl b/code/glTFAsset.inl index 6d328614e..a5d74f5a0 100644 --- a/code/glTFAsset.inl +++ b/code/glTFAsset.inl @@ -1282,7 +1282,7 @@ inline void Asset::ReadBinaryHeader(IOStream& stream) } AI_SWAP4(header.version); - asset.version = std::to_string(header.version); + asset.version = to_string(header.version); if (header.version != 1) { throw DeadlyImportError("GLTF: Unsupported binary glTF version"); } diff --git a/contrib/android-cmake/AndroidNdkGdb.cmake b/contrib/android-cmake/AndroidNdkGdb.cmake new file mode 100644 index 000000000..0677dcda9 --- /dev/null +++ b/contrib/android-cmake/AndroidNdkGdb.cmake @@ -0,0 +1,96 @@ +# Copyright (c) 2014, Pavel Rojtberg +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# ------------------------------------------------------------------------------ +# Usage: +# 1. place AndroidNdkGdb.cmake somewhere inside ${CMAKE_MODULE_PATH} +# 2. inside your project add +# +# include(AndroidNdkGdb) +# android_ndk_gdb_enable() +# # for each target +# add_library(MyLibrary ...) +# android_ndk_gdb_debuggable(MyLibrary) + + +# add gdbserver and general gdb configuration to project +# also create a mininal NDK skeleton so ndk-gdb finds the paths +# +# the optional parameter defines the path to the android project. +# uses PROJECT_SOURCE_DIR by default. +macro(android_ndk_gdb_enable) + if(ANDROID) + # create custom target that depends on the real target so it gets executed afterwards + add_custom_target(NDK_GDB ALL) + + if(${ARGC}) + set(ANDROID_PROJECT_DIR ${ARGV0}) + else() + set(ANDROID_PROJECT_DIR ${PROJECT_SOURCE_DIR}) + endif() + + set(NDK_GDB_SOLIB_PATH ${ANDROID_PROJECT_DIR}/obj/local/${ANDROID_NDK_ABI_NAME}/) + file(MAKE_DIRECTORY ${NDK_GDB_SOLIB_PATH}) + + # 1. generate essential Android Makefiles + file(MAKE_DIRECTORY ${ANDROID_PROJECT_DIR}/jni) + if(NOT EXISTS ${ANDROID_PROJECT_DIR}/jni/Android.mk) + file(WRITE ${ANDROID_PROJECT_DIR}/jni/Android.mk "APP_ABI := ${ANDROID_NDK_ABI_NAME}\n") + endif() + if(NOT EXISTS ${ANDROID_PROJECT_DIR}/jni/Application.mk) + file(WRITE ${ANDROID_PROJECT_DIR}/jni/Application.mk "APP_ABI := ${ANDROID_NDK_ABI_NAME}\n") + endif() + + # 2. generate gdb.setup + get_directory_property(PROJECT_INCLUDES DIRECTORY ${PROJECT_SOURCE_DIR} INCLUDE_DIRECTORIES) + string(REGEX REPLACE ";" " " PROJECT_INCLUDES "${PROJECT_INCLUDES}") + file(WRITE ${LIBRARY_OUTPUT_PATH}/gdb.setup "set solib-search-path ${NDK_GDB_SOLIB_PATH}\n") + file(APPEND ${LIBRARY_OUTPUT_PATH}/gdb.setup "directory ${PROJECT_INCLUDES}\n") + + # 3. copy gdbserver executable + file(COPY ${ANDROID_NDK}/prebuilt/android-${ANDROID_ARCH_NAME}/gdbserver/gdbserver DESTINATION ${LIBRARY_OUTPUT_PATH}) + endif() +endmacro() + +# register a target for remote debugging +# copies the debug version to NDK_GDB_SOLIB_PATH then strips symbols of original +macro(android_ndk_gdb_debuggable TARGET_NAME) + if(ANDROID) + get_property(TARGET_LOCATION TARGET ${TARGET_NAME} PROPERTY LOCATION) + + # create custom target that depends on the real target so it gets executed afterwards + add_dependencies(NDK_GDB ${TARGET_NAME}) + + # 4. copy lib to obj + add_custom_command(TARGET NDK_GDB POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${TARGET_LOCATION} ${NDK_GDB_SOLIB_PATH}) + + # 5. strip symbols + add_custom_command(TARGET NDK_GDB POST_BUILD COMMAND ${CMAKE_STRIP} ${TARGET_LOCATION}) + endif() +endmacro() diff --git a/contrib/android-cmake/AndroidNdkModules.cmake b/contrib/android-cmake/AndroidNdkModules.cmake new file mode 100644 index 000000000..64f37fdee --- /dev/null +++ b/contrib/android-cmake/AndroidNdkModules.cmake @@ -0,0 +1,58 @@ +# Copyright (c) 2014, Pavel Rojtberg +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +macro(android_ndk_import_module_cpufeatures) + if(ANDROID) + include_directories(${ANDROID_NDK}/sources/android/cpufeatures) + add_library(cpufeatures ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c) + target_link_libraries(cpufeatures dl) + endif() +endmacro() + +macro(android_ndk_import_module_native_app_glue) + if(ANDROID) + include_directories(${ANDROID_NDK}/sources/android/native_app_glue) + add_library(native_app_glue ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) + target_link_libraries(native_app_glue log) + endif() +endmacro() + +macro(android_ndk_import_module_ndk_helper) + if(ANDROID) + android_ndk_import_module_cpufeatures() + android_ndk_import_module_native_app_glue() + + include_directories(${ANDROID_NDK}/sources/android/ndk_helper) + file(GLOB _NDK_HELPER_SRCS ${ANDROID_NDK}/sources/android/ndk_helper/*.cpp ${ANDROID_NDK}/sources/android/ndk_helper/gl3stub.c) + add_library(ndk_helper ${_NDK_HELPER_SRCS}) + target_link_libraries(ndk_helper log android EGL GLESv2 cpufeatures native_app_glue) + + unset(_NDK_HELPER_SRCS) + endif() +endmacro() \ No newline at end of file diff --git a/contrib/android-cmake/README.md b/contrib/android-cmake/README.md new file mode 100644 index 000000000..ee6302128 --- /dev/null +++ b/contrib/android-cmake/README.md @@ -0,0 +1,240 @@ +# android-cmake + +CMake is great, and so is Android. This is a collection of CMake scripts that may be useful to the Android NDK community. It is based on experience from porting OpenCV library to Android: http://opencv.org/platforms/android.html + +Main goal is to share these scripts so that devs that use CMake as their build system may easily compile native code for Android. + +## TL;DR + + cmake -DCMAKE_TOOLCHAIN_FILE=android.toolchain.cmake \ + -DANDROID_NDK= \ + -DCMAKE_BUILD_TYPE=Release \ + -DANDROID_ABI="armeabi-v7a with NEON" \ + + cmake --build . + +One-liner: + + cmake -DCMAKE_TOOLCHAIN_FILE=android.toolchain.cmake -DANDROID_NDK= -DCMAKE_BUILD_TYPE=Release -DANDROID_ABI="armeabi-v7a with NEON" && cmake --build . + +_android-cmake_ will search for your NDK install in the following order: + +1. Value of `ANDROID_NDK` CMake variable; +1. Value of `ANDROID_NDK` environment variable; +1. Search under paths from `ANDROID_NDK_SEARCH_PATHS` CMake variable; +1. Search platform specific locations (home folder, Windows "Program Files", etc). + +So if you have installed the NDK as `~/android-ndk-r10d` then _android-cmake_ will locate it automatically. + +## Getting started + +To build a cmake-based C/C++ project for Android you need: + +* Android NDK (>= r5) http://developer.android.com/tools/sdk/ndk/index.html +* CMake (>= v2.6.3, >= v2.8.9 recommended) http://www.cmake.org/download + +The _android-cmake_ is also capable to build with NDK from AOSP or Linaro Android source tree, but you may be required to manually specify path to `libm` binary to link with. + +## Difference from traditional CMake + +Folowing the _ndk-build_ the _android-cmake_ supports **only two build targets**: + +* `-DCMAKE_BUILD_TYPE=Release` +* `-DCMAKE_BUILD_TYPE=Debug` + +So don't even try other targets that can be found in CMake documentation and don't forget to explicitly specify `Release` or `Debug` because CMake builds without a build configuration by default. + +## Difference from _ndk-build_ + +* Latest GCC available in NDK is used as the default compiler; +* `Release` builds with `-O3` instead of `-Os`; +* `Release` builds without debug info (without `-g`) (because _ndk-build_ always creates a stripped version but cmake delays this for `install/strip` target); +* `-fsigned-char` is added to compiler flags to make `char` signed by default as it is on x86/x86_64; +* GCC's stack protector is not used neither in `Debug` nor `Release` configurations; +* No builds for multiple platforms (e.g. building for both arm and x86 require to run cmake twice with different parameters); +* No file level Neon via `.neon` suffix; + +The following features of _ndk-build_ are not supported by the _android-cmake_ yet: + +* `armeabi-v7a-hard` ABI +* `libc++_static`/`libc++_shared` STL runtime + +## Basic options + +Similarly to the NDK build system _android-cmake_ allows to select between several compiler toolchains and target platforms. Most of the options can be set either as cmake arguments: `-D=` or as environment variables: + +* **ANDROID_NDK** - path to the Android NDK. If not set then _android-cmake_ will search for the most recent version of supported NDK in commonly used locations; +* **ANDROID_ABI** - specifies the target Application Binary Interface (ABI). This option nearly matches to the APP_ABI variable used by ndk-build tool from Android NDK. If not specified then set to `armeabi-v7a`. Possible target names are: + * `armeabi` - ARMv5TE based CPU with software floating point operations; + * **`armeabi-v7a`** - ARMv7 based devices with hardware FPU instructions (VFPv3_D16); + * `armeabi-v7a with NEON` - same as armeabi-v7a, but sets NEON as floating-point unit; + * `armeabi-v7a with VFPV3` - same as armeabi-v7a, but sets VFPv3_D32 as floating-point unit; + * `armeabi-v6 with VFP` - tuned for ARMv6 processors having VFP; + * `x86` - IA-32 instruction set + * `mips` - MIPS32 instruction set + * `arm64-v8a` - ARMv8 AArch64 instruction set - only for NDK r10 and newer + * `x86_64` - Intel64 instruction set (r1) - only for NDK r10 and newer + * `mips64` - MIPS64 instruction set (r6) - only for NDK r10 and newer +* **ANDROID_NATIVE_API_LEVEL** - level of android API to build for. Can be set either to full name (example: `android-8`) or a numeric value (example: `17`). The default API level depends on the target ABI: + * `android-8` for ARM; + * `android-9` for x86 and MIPS; + * `android-21` for 64-bit ABIs. + + Building for `android-L` is possible only when it is explicitly selected. +* **ANDROID_TOOLCHAIN_NAME** - the name of compiler toolchain to be used. This option allows to select between different GCC and Clang versions. The list of possible values depends on the NDK version and will be printed by toolchain file if an invalid value is set. By default _android-cmake_ selects the most recent version of GCC which can build for specified `ANDROID_ABI`. + + Example values are: + * `aarch64-linux-android-4.9` + * `aarch64-linux-android-clang3.5` + * `arm-linux-androideabi-4.8` + * `arm-linux-androideabi-4.9` + * `arm-linux-androideabi-clang3.5` + * `mips64el-linux-android-4.9` + * `mipsel-linux-android-4.8` + * `x86-4.9` + * `x86_64-4.9` + * etc. +* **ANDROID_STL** - the name of C++ runtime to use. The default is `gnustl_static`. + * `none` - do not configure the runtime. + * `system` - use the default minimal system C++ runtime library. + * Implies `-fno-rtti -fno-exceptions`. + * `system_re` - use the default minimal system C++ runtime library. + * Implies `-frtti -fexceptions`. + * `gabi++_static` - use the GAbi++ runtime as a static library. + * Implies `-frtti -fno-exceptions`. + * Available for NDK r7 and newer. + * `gabi++_shared` - use the GAbi++ runtime as a shared library. + * Implies `-frtti -fno-exceptions`. + * Available for NDK r7 and newer. + * `stlport_static` - use the STLport runtime as a static library. + * Implies `-fno-rtti -fno-exceptions` for NDK before r7. + * Implies `-frtti -fno-exceptions` for NDK r7 and newer. + * `stlport_shared` - use the STLport runtime as a shared library. + * Implies `-fno-rtti -fno-exceptions` for NDK before r7. + * Implies `-frtti -fno-exceptions` for NDK r7 and newer. + * **`gnustl_static`** - use the GNU STL as a static library. + * Implies `-frtti -fexceptions`. + * `gnustl_shared` - use the GNU STL as a shared library. + * Implies `-frtti -fno-exceptions`. + * Available for NDK r7b and newer. + * Silently degrades to `gnustl_static` if not available. +* **NDK_CCACHE** - path to `ccache` executable. If not set then initialized from `NDK_CCACHE` environment variable. + +## Advanced _android-cmake_ options + +Normally _android-cmake_ users are not supposed to touch these variables but they might be useful to workaround some build issues: + +* **ANDROID_FORCE_ARM_BUILD** = `OFF` - generate 32-bit ARM instructions instead of Thumb. Applicable only for arm ABIs and is forced to be `ON` for `armeabi-v6 with VFP`; +* **ANDROID_NO_UNDEFINED** = `ON` - show all undefined symbols as linker errors; +* **ANDROID_SO_UNDEFINED** = `OFF` - allow undefined symbols in shared libraries; + * actually it is turned `ON` by default for NDK older than `r7` +* **ANDROID_STL_FORCE_FEATURES** = `ON` - automatically configure rtti and exceptions support based on C++ runtime; +* **ANDROID_NDK_LAYOUT** = `RELEASE` - inner layout of Android NDK, should be detected automatically. Possible values are: + * `RELEASE` - public releases from Google; + * `LINARO` - NDK from Linaro project; + * `ANDROID` - NDK from AOSP. +* **ANDROID_FUNCTION_LEVEL_LINKING** = `ON` - enables saparate putting each function and data items into separate sections and enable garbage collection of unused input sections at link time (`-fdata-sections -ffunction-sections -Wl,--gc-sections`); +* **ANDROID_GOLD_LINKER** = `ON` - use gold linker with GCC 4.6 for NDK r8b and newer (only for ARM and x86); +* **ANDROID_NOEXECSTACK** = `ON` - enables or disables stack execution protection code (`-Wl,-z,noexecstack`); +* **ANDROID_RELRO** = `ON` - Enables RELRO - a memory corruption mitigation technique (`-Wl,-z,relro -Wl,-z,now`); +* **ANDROID_LIBM_PATH** - path to `libm.so` (set to something like `$(TOP)/out/target/product//obj/lib/libm.so`) to workaround unresolved `sincos`. + +## Fine-tuning `CMakeLists.txt` for _android-cmake_ + +### Recognizing Android build + +_android-cmake_ defines `ANDROID` CMake variable which can be used to add Android-specific stuff: + + if (ANDROID) + message(STATUS "Hello from Android build!") + endif() + +The recommended way to identify ARM/MIPS/x86 architecture is examining `CMAKE_SYSTEM_PROCESSOR` which is set to the appropriate value: + +* `armv5te` - for `armeabi` ABI +* `armv6` - for `armeabi-v6 with VFP` ABI +* `armv7-a` - for `armeabi-v7a`, `armeabi-v7a with VFPV3` and `armeabi-v7a with NEON` ABIs +* `aarch64` - for `arm64-v8a` ABI +* `i686` - for `x86` ABI +* `x86_64` - for `x86_64` ABI +* `mips` - for `mips` ABI +* `mips64` - for `mips64` ABI + +Other variables that are set by _android-cmake_ and can be used for the fine-grained build configuration are: + +* `NEON` - set if target ABI supports Neon; +* `ANDROID_NATIVE_API_LEVEL` - native Android API level we are building for (note: Java part of Andoid application can be built for another API level) +* `ANDROID_NDK_RELEASE` - version of the Android NDK +* `ANDROID_NDK_HOST_SYSTEM_NAME` - "windows", "linux-x86" or "darwin-x86" depending on the host platform +* `ANDROID_RTTI` - set if rtti is enabled by the runtime +* `ANDROID_EXCEPTIONS` - set if exceptions are enabled by the runtime + +### Finding packages + +When crosscompiling CMake `find_*` commands are normally expected to find libraries and packages belonging to the same build target. So _android-cmake_ configures CMake to search in Android-specific paths only and ignore your host system locations. So + + find_package(ZLIB) + +will surely find libz.so within the Android NDK. + +However sometimes you need to locate a host package even when cross-compiling. For example you can be searching for your documentation generator. The _android-cmake_ recommends you to use `find_host_package` and `find_host_program` macro defined in the `android.toolchain.cmake`: + + find_host_package(Doxygen) + find_host_program(PDFLATEX pdflatex) + +However this will break regular builds so instead of wrapping package search into platform-specific logic you can copy the following snippet into your project (put it after your top-level `project()` command): + + # Search packages for host system instead of packages for target system + # in case of cross compilation these macro should be defined by toolchain file + if(NOT COMMAND find_host_package) + macro(find_host_package) + find_package(${ARGN}) + endmacro() + endif() + if(NOT COMMAND find_host_program) + macro(find_host_program) + find_program(${ARGN}) + endmacro() + endif() + +### Compiler flags recycling + +Make sure to do the following in your scripts: + + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${my_cxx_flags}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${my_cxx_flags}") + +The flags will be prepopulated with critical flags, so don't loose them. Also be aware that _android-cmake_ also sets configuration-specific compiler and linker flags. + +## Troubleshooting + +### Building on Windows + +First of all `cygwin` builds are **NOT supported** and will not be supported by _android-cmake_. To build natively on Windows you need a port of make but I recommend http://martine.github.io/ninja/ instead. + +To build with Ninja you need: + +* Ensure you are using CMake newer than 2.8.9; +* Download the latest Ninja from https://github.com/martine/ninja/releases; +* Put the `ninja.exe` into your PATH (or add path to `ninja.exe` to your PATH environment variable); +* Pass `-GNinja` to `cmake` alongside with other arguments (or choose Ninja generator in `cmake-gui`). +* Enjoy the fast native multithreaded build :) + +But if you still want to stick to old make then: + +* Get a Windows port of GNU Make: + * Android NDK r7 (and newer) already has `make.exe` on board; + * `mingw-make` should work as fine; + * Download some other port. For example, this one: http://gnuwin32.sourceforge.net/packages/make.htm. +* Add path to your `make.exe` to system PATH or always use full path; +* Pass `-G"MinGW Makefiles"` and `-DCMAKE_MAKE_PROGRAM="make.exe"` + * It must be `MinGW Makefiles` and not `Unix Makefiles` even if your `make.exe` is not a MinGW's make. +* Run `make.exe` or `cmake --build .` for single-threaded build. + +### Projects with assembler files + +The _android-cmake_ should correctly handle projects with assembler sources (`*.s` or `*.S`). But if you still facing problems with assembler then try to upgrade your CMake to version newer than 2.8.5 + +## Copying + +_android-cmake_ is distributed under the terms of [BSD 3-Clause License](http://opensource.org/licenses/BSD-3-Clause) \ No newline at end of file diff --git a/contrib/android-cmake/android.toolchain.cmake b/contrib/android-cmake/android.toolchain.cmake new file mode 100644 index 000000000..ffa26126a --- /dev/null +++ b/contrib/android-cmake/android.toolchain.cmake @@ -0,0 +1,1693 @@ +# Copyright (c) 2010-2011, Ethan Rublee +# Copyright (c) 2011-2014, Andrey Kamaev +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# ------------------------------------------------------------------------------ +# Android CMake toolchain file, for use with the Android NDK r5-r10d +# Requires cmake 2.6.3 or newer (2.8.9 or newer is recommended). +# See home page: https://github.com/taka-no-me/android-cmake +# +# Usage Linux: +# $ export ANDROID_NDK=/absolute/path/to/the/android-ndk +# $ mkdir build && cd build +# $ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/the/android.toolchain.cmake .. +# $ make -j8 +# +# Usage Windows: +# You need native port of make to build your project. +# Android NDK r7 (and newer) already has make.exe on board. +# For older NDK you have to install it separately. +# For example, this one: http://gnuwin32.sourceforge.net/packages/make.htm +# +# $ SET ANDROID_NDK=C:\absolute\path\to\the\android-ndk +# $ mkdir build && cd build +# $ cmake.exe -G"MinGW Makefiles" +# -DCMAKE_TOOLCHAIN_FILE=path\to\the\android.toolchain.cmake +# -DCMAKE_MAKE_PROGRAM="%ANDROID_NDK%\prebuilt\windows\bin\make.exe" .. +# $ cmake.exe --build . +# +# +# Options (can be set as cmake parameters: -D=): +# ANDROID_NDK=/opt/android-ndk - path to the NDK root. +# Can be set as environment variable. Can be set only at first cmake run. +# +# ANDROID_ABI=armeabi-v7a - specifies the target Application Binary +# Interface (ABI). This option nearly matches to the APP_ABI variable +# used by ndk-build tool from Android NDK. +# +# Possible targets are: +# "armeabi" - ARMv5TE based CPU with software floating point operations +# "armeabi-v7a" - ARMv7 based devices with hardware FPU instructions +# this ABI target is used by default +# "armeabi-v7a with NEON" - same as armeabi-v7a, but +# sets NEON as floating-point unit +# "armeabi-v7a with VFPV3" - same as armeabi-v7a, but +# sets VFPV3 as floating-point unit (has 32 registers instead of 16) +# "armeabi-v6 with VFP" - tuned for ARMv6 processors having VFP +# "x86" - IA-32 instruction set +# "mips" - MIPS32 instruction set +# +# 64-bit ABIs for NDK r10 and newer: +# "arm64-v8a" - ARMv8 AArch64 instruction set +# "x86_64" - Intel64 instruction set (r1) +# "mips64" - MIPS64 instruction set (r6) +# +# ANDROID_NATIVE_API_LEVEL=android-8 - level of Android API compile for. +# Option is read-only when standalone toolchain is used. +# Note: building for "android-L" requires explicit configuration. +# +# ANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-4.9 - the name of compiler +# toolchain to be used. The list of possible values depends on the NDK +# version. For NDK r10c the possible values are: +# +# * aarch64-linux-android-4.9 +# * aarch64-linux-android-clang3.4 +# * aarch64-linux-android-clang3.5 +# * arm-linux-androideabi-4.6 +# * arm-linux-androideabi-4.8 +# * arm-linux-androideabi-4.9 (default) +# * arm-linux-androideabi-clang3.4 +# * arm-linux-androideabi-clang3.5 +# * mips64el-linux-android-4.9 +# * mips64el-linux-android-clang3.4 +# * mips64el-linux-android-clang3.5 +# * mipsel-linux-android-4.6 +# * mipsel-linux-android-4.8 +# * mipsel-linux-android-4.9 +# * mipsel-linux-android-clang3.4 +# * mipsel-linux-android-clang3.5 +# * x86-4.6 +# * x86-4.8 +# * x86-4.9 +# * x86-clang3.4 +# * x86-clang3.5 +# * x86_64-4.9 +# * x86_64-clang3.4 +# * x86_64-clang3.5 +# +# ANDROID_FORCE_ARM_BUILD=OFF - set ON to generate 32-bit ARM instructions +# instead of Thumb. Is not available for "armeabi-v6 with VFP" +# (is forced to be ON) ABI. +# +# ANDROID_NO_UNDEFINED=ON - set ON to show all undefined symbols as linker +# errors even if they are not used. +# +# ANDROID_SO_UNDEFINED=OFF - set ON to allow undefined symbols in shared +# libraries. Automatically turned for NDK r5x and r6x due to GLESv2 +# problems. +# +# ANDROID_STL=gnustl_static - specify the runtime to use. +# +# Possible values are: +# none -> Do not configure the runtime. +# system -> Use the default minimal system C++ runtime library. +# Implies -fno-rtti -fno-exceptions. +# Is not available for standalone toolchain. +# system_re -> Use the default minimal system C++ runtime library. +# Implies -frtti -fexceptions. +# Is not available for standalone toolchain. +# gabi++_static -> Use the GAbi++ runtime as a static library. +# Implies -frtti -fno-exceptions. +# Available for NDK r7 and newer. +# Is not available for standalone toolchain. +# gabi++_shared -> Use the GAbi++ runtime as a shared library. +# Implies -frtti -fno-exceptions. +# Available for NDK r7 and newer. +# Is not available for standalone toolchain. +# stlport_static -> Use the STLport runtime as a static library. +# Implies -fno-rtti -fno-exceptions for NDK before r7. +# Implies -frtti -fno-exceptions for NDK r7 and newer. +# Is not available for standalone toolchain. +# stlport_shared -> Use the STLport runtime as a shared library. +# Implies -fno-rtti -fno-exceptions for NDK before r7. +# Implies -frtti -fno-exceptions for NDK r7 and newer. +# Is not available for standalone toolchain. +# gnustl_static -> Use the GNU STL as a static library. +# Implies -frtti -fexceptions. +# gnustl_shared -> Use the GNU STL as a shared library. +# Implies -frtti -fno-exceptions. +# Available for NDK r7b and newer. +# Silently degrades to gnustl_static if not available. +# +# ANDROID_STL_FORCE_FEATURES=ON - turn rtti and exceptions support based on +# chosen runtime. If disabled, then the user is responsible for settings +# these options. +# +# What?: +# android-cmake toolchain searches for NDK/toolchain in the following order: +# ANDROID_NDK - cmake parameter +# ANDROID_NDK - environment variable +# ANDROID_STANDALONE_TOOLCHAIN - cmake parameter +# ANDROID_STANDALONE_TOOLCHAIN - environment variable +# ANDROID_NDK - default locations +# ANDROID_STANDALONE_TOOLCHAIN - default locations +# +# Make sure to do the following in your scripts: +# SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${my_cxx_flags}" ) +# SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${my_cxx_flags}" ) +# The flags will be prepopulated with critical flags, so don't loose them. +# Also be aware that toolchain also sets configuration-specific compiler +# flags and linker flags. +# +# ANDROID and BUILD_ANDROID will be set to true, you may test any of these +# variables to make necessary Android-specific configuration changes. +# +# Also ARMEABI or ARMEABI_V7A or X86 or MIPS or ARM64_V8A or X86_64 or MIPS64 +# will be set true, mutually exclusive. NEON option will be set true +# if VFP is set to NEON. +# +# ------------------------------------------------------------------------------ + +cmake_minimum_required( VERSION 2.6.3 ) + +if( DEFINED CMAKE_CROSSCOMPILING ) + # subsequent toolchain loading is not really needed + return() +endif() + +if( CMAKE_TOOLCHAIN_FILE ) + # touch toolchain variable to suppress "unused variable" warning +endif() + +# inherit settings in recursive loads +get_property( _CMAKE_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE ) +if( _CMAKE_IN_TRY_COMPILE ) + include( "${CMAKE_CURRENT_SOURCE_DIR}/../android.toolchain.config.cmake" OPTIONAL ) +endif() + +# this one is important +if( CMAKE_VERSION VERSION_GREATER "3.0.99" ) + set( CMAKE_SYSTEM_NAME Android ) +else() + set( CMAKE_SYSTEM_NAME Linux ) +endif() + +# this one not so much +set( CMAKE_SYSTEM_VERSION 1 ) + +# rpath makes low sense for Android +set( CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "" ) +set( CMAKE_SKIP_RPATH TRUE CACHE BOOL "If set, runtime paths are not added when using shared libraries." ) + +# NDK search paths +set( ANDROID_SUPPORTED_NDK_VERSIONS ${ANDROID_EXTRA_NDK_VERSIONS} -r10d -r10c -r10b -r10 -r9d -r9c -r9b -r9 -r8e -r8d -r8c -r8b -r8 -r7c -r7b -r7 -r6b -r6 -r5c -r5b -r5 "" ) +if( NOT DEFINED ANDROID_NDK_SEARCH_PATHS ) + if( CMAKE_HOST_WIN32 ) + file( TO_CMAKE_PATH "$ENV{PROGRAMFILES}" ANDROID_NDK_SEARCH_PATHS ) + set( ANDROID_NDK_SEARCH_PATHS "${ANDROID_NDK_SEARCH_PATHS}" "$ENV{SystemDrive}/NVPACK" ) + else() + file( TO_CMAKE_PATH "$ENV{HOME}" ANDROID_NDK_SEARCH_PATHS ) + set( ANDROID_NDK_SEARCH_PATHS /opt "${ANDROID_NDK_SEARCH_PATHS}/NVPACK" ) + endif() +endif() +if( NOT DEFINED ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH ) + set( ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH /opt/android-toolchain ) +endif() + +# known ABIs +set( ANDROID_SUPPORTED_ABIS_arm "armeabi-v7a;armeabi;armeabi-v7a with NEON;armeabi-v7a with VFPV3;armeabi-v6 with VFP" ) +set( ANDROID_SUPPORTED_ABIS_arm64 "arm64-v8a" ) +set( ANDROID_SUPPORTED_ABIS_x86 "x86" ) +set( ANDROID_SUPPORTED_ABIS_x86_64 "x86_64" ) +set( ANDROID_SUPPORTED_ABIS_mips "mips" ) +set( ANDROID_SUPPORTED_ABIS_mips64 "mips64" ) + +# API level defaults +set( ANDROID_DEFAULT_NDK_API_LEVEL 8 ) +set( ANDROID_DEFAULT_NDK_API_LEVEL_arm64 21 ) +set( ANDROID_DEFAULT_NDK_API_LEVEL_x86 9 ) +set( ANDROID_DEFAULT_NDK_API_LEVEL_x86_64 21 ) +set( ANDROID_DEFAULT_NDK_API_LEVEL_mips 9 ) +set( ANDROID_DEFAULT_NDK_API_LEVEL_mips64 21 ) + + +macro( __LIST_FILTER listvar regex ) + if( ${listvar} ) + foreach( __val ${${listvar}} ) + if( __val MATCHES "${regex}" ) + list( REMOVE_ITEM ${listvar} "${__val}" ) + endif() + endforeach() + endif() +endmacro() + +macro( __INIT_VARIABLE var_name ) + set( __test_path 0 ) + foreach( __var ${ARGN} ) + if( __var STREQUAL "PATH" ) + set( __test_path 1 ) + break() + endif() + endforeach() + + if( __test_path AND NOT EXISTS "${${var_name}}" ) + unset( ${var_name} CACHE ) + endif() + + if( " ${${var_name}}" STREQUAL " " ) + set( __values 0 ) + foreach( __var ${ARGN} ) + if( __var STREQUAL "VALUES" ) + set( __values 1 ) + elseif( NOT __var STREQUAL "PATH" ) + if( __var MATCHES "^ENV_.*$" ) + string( REPLACE "ENV_" "" __var "${__var}" ) + set( __value "$ENV{${__var}}" ) + elseif( DEFINED ${__var} ) + set( __value "${${__var}}" ) + elseif( __values ) + set( __value "${__var}" ) + else() + set( __value "" ) + endif() + + if( NOT " ${__value}" STREQUAL " " AND (NOT __test_path OR EXISTS "${__value}") ) + set( ${var_name} "${__value}" ) + break() + endif() + endif() + endforeach() + unset( __value ) + unset( __values ) + endif() + + if( __test_path ) + file( TO_CMAKE_PATH "${${var_name}}" ${var_name} ) + endif() + unset( __test_path ) +endmacro() + +macro( __DETECT_NATIVE_API_LEVEL _var _path ) + set( __ndkApiLevelRegex "^[\t ]*#define[\t ]+__ANDROID_API__[\t ]+([0-9]+)[\t ]*.*$" ) + file( STRINGS ${_path} __apiFileContent REGEX "${__ndkApiLevelRegex}" ) + if( NOT __apiFileContent ) + message( SEND_ERROR "Could not get Android native API level. Probably you have specified invalid level value, or your copy of NDK/toolchain is broken." ) + endif() + string( REGEX REPLACE "${__ndkApiLevelRegex}" "\\1" ${_var} "${__apiFileContent}" ) + unset( __apiFileContent ) + unset( __ndkApiLevelRegex ) +endmacro() + +macro( __DETECT_TOOLCHAIN_MACHINE_NAME _var _root ) + if( EXISTS "${_root}" ) + file( GLOB __gccExePath RELATIVE "${_root}/bin/" "${_root}/bin/*-gcc${TOOL_OS_SUFFIX}" ) + __LIST_FILTER( __gccExePath "^[.].*" ) + list( LENGTH __gccExePath __gccExePathsCount ) + if( NOT __gccExePathsCount EQUAL 1 AND NOT _CMAKE_IN_TRY_COMPILE ) + message( WARNING "Could not determine machine name for compiler from ${_root}" ) + set( ${_var} "" ) + else() + get_filename_component( __gccExeName "${__gccExePath}" NAME_WE ) + string( REPLACE "-gcc" "" ${_var} "${__gccExeName}" ) + endif() + unset( __gccExePath ) + unset( __gccExePathsCount ) + unset( __gccExeName ) + else() + set( ${_var} "" ) + endif() +endmacro() + + +# fight against cygwin +set( ANDROID_FORBID_SYGWIN TRUE CACHE BOOL "Prevent cmake from working under cygwin and using cygwin tools") +mark_as_advanced( ANDROID_FORBID_SYGWIN ) +if( ANDROID_FORBID_SYGWIN ) + if( CYGWIN ) + message( FATAL_ERROR "Android NDK and android-cmake toolchain are not welcome Cygwin. It is unlikely that this cmake toolchain will work under cygwin. But if you want to try then you can set cmake variable ANDROID_FORBID_SYGWIN to FALSE and rerun cmake." ) + endif() + + if( CMAKE_HOST_WIN32 ) + # remove cygwin from PATH + set( __new_path "$ENV{PATH}") + __LIST_FILTER( __new_path "cygwin" ) + set(ENV{PATH} "${__new_path}") + unset(__new_path) + endif() +endif() + + +# detect current host platform +if( NOT DEFINED ANDROID_NDK_HOST_X64 AND (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|AMD64" OR CMAKE_HOST_APPLE) ) + set( ANDROID_NDK_HOST_X64 1 CACHE BOOL "Try to use 64-bit compiler toolchain" ) + mark_as_advanced( ANDROID_NDK_HOST_X64 ) +endif() + +set( TOOL_OS_SUFFIX "" ) +if( CMAKE_HOST_APPLE ) + set( ANDROID_NDK_HOST_SYSTEM_NAME "darwin-x86_64" ) + set( ANDROID_NDK_HOST_SYSTEM_NAME2 "darwin-x86" ) +elseif( CMAKE_HOST_WIN32 ) + set( ANDROID_NDK_HOST_SYSTEM_NAME "windows-x86_64" ) + set( ANDROID_NDK_HOST_SYSTEM_NAME2 "windows" ) + set( TOOL_OS_SUFFIX ".exe" ) +elseif( CMAKE_HOST_UNIX ) + set( ANDROID_NDK_HOST_SYSTEM_NAME "linux-x86_64" ) + set( ANDROID_NDK_HOST_SYSTEM_NAME2 "linux-x86" ) +else() + message( FATAL_ERROR "Cross-compilation on your platform is not supported by this cmake toolchain" ) +endif() + +if( NOT ANDROID_NDK_HOST_X64 ) + set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) +endif() + +# see if we have path to Android NDK +if( NOT ANDROID_NDK AND NOT ANDROID_STANDALONE_TOOLCHAIN ) + __INIT_VARIABLE( ANDROID_NDK PATH ENV_ANDROID_NDK ) +endif() +if( NOT ANDROID_NDK ) + # see if we have path to Android standalone toolchain + __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ENV_ANDROID_STANDALONE_TOOLCHAIN ) + + if( NOT ANDROID_STANDALONE_TOOLCHAIN ) + #try to find Android NDK in one of the the default locations + set( __ndkSearchPaths ) + foreach( __ndkSearchPath ${ANDROID_NDK_SEARCH_PATHS} ) + foreach( suffix ${ANDROID_SUPPORTED_NDK_VERSIONS} ) + list( APPEND __ndkSearchPaths "${__ndkSearchPath}/android-ndk${suffix}" ) + endforeach() + endforeach() + __INIT_VARIABLE( ANDROID_NDK PATH VALUES ${__ndkSearchPaths} ) + unset( __ndkSearchPaths ) + + if( ANDROID_NDK ) + message( STATUS "Using default path for Android NDK: ${ANDROID_NDK}" ) + message( STATUS " If you prefer to use a different location, please define a cmake or environment variable: ANDROID_NDK" ) + else() + #try to find Android standalone toolchain in one of the the default locations + __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH ) + + if( ANDROID_STANDALONE_TOOLCHAIN ) + message( STATUS "Using default path for standalone toolchain ${ANDROID_STANDALONE_TOOLCHAIN}" ) + message( STATUS " If you prefer to use a different location, please define the variable: ANDROID_STANDALONE_TOOLCHAIN" ) + endif( ANDROID_STANDALONE_TOOLCHAIN ) + endif( ANDROID_NDK ) + endif( NOT ANDROID_STANDALONE_TOOLCHAIN ) +endif( NOT ANDROID_NDK ) + +# remember found paths +if( ANDROID_NDK ) + get_filename_component( ANDROID_NDK "${ANDROID_NDK}" ABSOLUTE ) + set( ANDROID_NDK "${ANDROID_NDK}" CACHE INTERNAL "Path of the Android NDK" FORCE ) + set( BUILD_WITH_ANDROID_NDK True ) + if( EXISTS "${ANDROID_NDK}/RELEASE.TXT" ) + file( STRINGS "${ANDROID_NDK}/RELEASE.TXT" ANDROID_NDK_RELEASE_FULL LIMIT_COUNT 1 REGEX "r[0-9]+[a-z]?" ) + string( REGEX MATCH "r([0-9]+)([a-z]?)" ANDROID_NDK_RELEASE "${ANDROID_NDK_RELEASE_FULL}" ) + else() + set( ANDROID_NDK_RELEASE "r1x" ) + set( ANDROID_NDK_RELEASE_FULL "unreleased" ) + endif() + string( REGEX REPLACE "r([0-9]+)([a-z]?)" "\\1*1000" ANDROID_NDK_RELEASE_NUM "${ANDROID_NDK_RELEASE}" ) + string( FIND " abcdefghijklmnopqastuvwxyz" "${CMAKE_MATCH_2}" __ndkReleaseLetterNum ) + math( EXPR ANDROID_NDK_RELEASE_NUM "${ANDROID_NDK_RELEASE_NUM}+${__ndkReleaseLetterNum}" ) +elseif( ANDROID_STANDALONE_TOOLCHAIN ) + get_filename_component( ANDROID_STANDALONE_TOOLCHAIN "${ANDROID_STANDALONE_TOOLCHAIN}" ABSOLUTE ) + # try to detect change + if( CMAKE_AR ) + string( LENGTH "${ANDROID_STANDALONE_TOOLCHAIN}" __length ) + string( SUBSTRING "${CMAKE_AR}" 0 ${__length} __androidStandaloneToolchainPreviousPath ) + if( NOT __androidStandaloneToolchainPreviousPath STREQUAL ANDROID_STANDALONE_TOOLCHAIN ) + message( FATAL_ERROR "It is not possible to change path to the Android standalone toolchain on subsequent run." ) + endif() + unset( __androidStandaloneToolchainPreviousPath ) + unset( __length ) + endif() + set( ANDROID_STANDALONE_TOOLCHAIN "${ANDROID_STANDALONE_TOOLCHAIN}" CACHE INTERNAL "Path of the Android standalone toolchain" FORCE ) + set( BUILD_WITH_STANDALONE_TOOLCHAIN True ) +else() + list(GET ANDROID_NDK_SEARCH_PATHS 0 ANDROID_NDK_SEARCH_PATH) + message( FATAL_ERROR "Could not find neither Android NDK nor Android standalone toolchain. + You should either set an environment variable: + export ANDROID_NDK=~/my-android-ndk + or + export ANDROID_STANDALONE_TOOLCHAIN=~/my-android-toolchain + or put the toolchain or NDK in the default path: + sudo ln -s ~/my-android-ndk ${ANDROID_NDK_SEARCH_PATH}/android-ndk + sudo ln -s ~/my-android-toolchain ${ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH}" ) +endif() + +# android NDK layout +if( BUILD_WITH_ANDROID_NDK ) + if( NOT DEFINED ANDROID_NDK_LAYOUT ) + # try to automatically detect the layout + if( EXISTS "${ANDROID_NDK}/RELEASE.TXT") + set( ANDROID_NDK_LAYOUT "RELEASE" ) + elseif( EXISTS "${ANDROID_NDK}/../../linux-x86/toolchain/" ) + set( ANDROID_NDK_LAYOUT "LINARO" ) + elseif( EXISTS "${ANDROID_NDK}/../../gcc/" ) + set( ANDROID_NDK_LAYOUT "ANDROID" ) + endif() + endif() + set( ANDROID_NDK_LAYOUT "${ANDROID_NDK_LAYOUT}" CACHE STRING "The inner layout of NDK" ) + mark_as_advanced( ANDROID_NDK_LAYOUT ) + if( ANDROID_NDK_LAYOUT STREQUAL "LINARO" ) + set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) # only 32-bit at the moment + set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/../../${ANDROID_NDK_HOST_SYSTEM_NAME}/toolchain" ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH "" ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "" ) + elseif( ANDROID_NDK_LAYOUT STREQUAL "ANDROID" ) + set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) # only 32-bit at the moment + set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/../../gcc/${ANDROID_NDK_HOST_SYSTEM_NAME}/arm" ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH "" ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "" ) + else() # ANDROID_NDK_LAYOUT STREQUAL "RELEASE" + set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/toolchains" ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH "/prebuilt/${ANDROID_NDK_HOST_SYSTEM_NAME}" ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "/prebuilt/${ANDROID_NDK_HOST_SYSTEM_NAME2}" ) + endif() + get_filename_component( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK_TOOLCHAINS_PATH}" ABSOLUTE ) + + # try to detect change of NDK + if( CMAKE_AR ) + string( LENGTH "${ANDROID_NDK_TOOLCHAINS_PATH}" __length ) + string( SUBSTRING "${CMAKE_AR}" 0 ${__length} __androidNdkPreviousPath ) + if( NOT __androidNdkPreviousPath STREQUAL ANDROID_NDK_TOOLCHAINS_PATH ) + message( FATAL_ERROR "It is not possible to change the path to the NDK on subsequent CMake run. You must remove all generated files from your build folder first. + " ) + endif() + unset( __androidNdkPreviousPath ) + unset( __length ) + endif() +endif() + + +# get all the details about standalone toolchain +if( BUILD_WITH_STANDALONE_TOOLCHAIN ) + __DETECT_NATIVE_API_LEVEL( ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot/usr/include/android/api-level.h" ) + set( ANDROID_STANDALONE_TOOLCHAIN_API_LEVEL ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) + set( __availableToolchains "standalone" ) + __DETECT_TOOLCHAIN_MACHINE_NAME( __availableToolchainMachines "${ANDROID_STANDALONE_TOOLCHAIN}" ) + if( NOT __availableToolchainMachines ) + message( FATAL_ERROR "Could not determine machine name of your toolchain. Probably your Android standalone toolchain is broken." ) + endif() + if( __availableToolchainMachines MATCHES x86_64 ) + set( __availableToolchainArchs "x86_64" ) + elseif( __availableToolchainMachines MATCHES i686 ) + set( __availableToolchainArchs "x86" ) + elseif( __availableToolchainMachines MATCHES aarch64 ) + set( __availableToolchainArchs "arm64" ) + elseif( __availableToolchainMachines MATCHES arm ) + set( __availableToolchainArchs "arm" ) + elseif( __availableToolchainMachines MATCHES mips64el ) + set( __availableToolchainArchs "mips64" ) + elseif( __availableToolchainMachines MATCHES mipsel ) + set( __availableToolchainArchs "mips" ) + endif() + execute_process( COMMAND "${ANDROID_STANDALONE_TOOLCHAIN}/bin/${__availableToolchainMachines}-gcc${TOOL_OS_SUFFIX}" -dumpversion + OUTPUT_VARIABLE __availableToolchainCompilerVersions OUTPUT_STRIP_TRAILING_WHITESPACE ) + string( REGEX MATCH "[0-9]+[.][0-9]+([.][0-9]+)?" __availableToolchainCompilerVersions "${__availableToolchainCompilerVersions}" ) + if( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/bin/clang${TOOL_OS_SUFFIX}" ) + list( APPEND __availableToolchains "standalone-clang" ) + list( APPEND __availableToolchainMachines ${__availableToolchainMachines} ) + list( APPEND __availableToolchainArchs ${__availableToolchainArchs} ) + list( APPEND __availableToolchainCompilerVersions ${__availableToolchainCompilerVersions} ) + endif() +endif() + +macro( __GLOB_NDK_TOOLCHAINS __availableToolchainsVar __availableToolchainsLst __toolchain_subpath ) + foreach( __toolchain ${${__availableToolchainsLst}} ) + if( "${__toolchain}" MATCHES "-clang3[.][0-9]$" AND NOT EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/${__toolchain}${__toolchain_subpath}" ) + SET( __toolchainVersionRegex "^TOOLCHAIN_VERSION[\t ]+:=[\t ]+(.*)$" ) + FILE( STRINGS "${ANDROID_NDK_TOOLCHAINS_PATH}/${__toolchain}/setup.mk" __toolchainVersionStr REGEX "${__toolchainVersionRegex}" ) + if( __toolchainVersionStr ) + string( REGEX REPLACE "${__toolchainVersionRegex}" "\\1" __toolchainVersionStr "${__toolchainVersionStr}" ) + string( REGEX REPLACE "-clang3[.][0-9]$" "-${__toolchainVersionStr}" __gcc_toolchain "${__toolchain}" ) + else() + string( REGEX REPLACE "-clang3[.][0-9]$" "-4.6" __gcc_toolchain "${__toolchain}" ) + endif() + unset( __toolchainVersionStr ) + unset( __toolchainVersionRegex ) + else() + set( __gcc_toolchain "${__toolchain}" ) + endif() + __DETECT_TOOLCHAIN_MACHINE_NAME( __machine "${ANDROID_NDK_TOOLCHAINS_PATH}/${__gcc_toolchain}${__toolchain_subpath}" ) + if( __machine ) + string( REGEX MATCH "[0-9]+[.][0-9]+([.][0-9x]+)?$" __version "${__gcc_toolchain}" ) + if( __machine MATCHES x86_64 ) + set( __arch "x86_64" ) + elseif( __machine MATCHES i686 ) + set( __arch "x86" ) + elseif( __machine MATCHES aarch64 ) + set( __arch "arm64" ) + elseif( __machine MATCHES arm ) + set( __arch "arm" ) + elseif( __machine MATCHES mips64el ) + set( __arch "mips64" ) + elseif( __machine MATCHES mipsel ) + set( __arch "mips" ) + else() + set( __arch "" ) + endif() + #message("machine: !${__machine}!\narch: !${__arch}!\nversion: !${__version}!\ntoolchain: !${__toolchain}!\n") + if (__arch) + list( APPEND __availableToolchainMachines "${__machine}" ) + list( APPEND __availableToolchainArchs "${__arch}" ) + list( APPEND __availableToolchainCompilerVersions "${__version}" ) + list( APPEND ${__availableToolchainsVar} "${__toolchain}" ) + endif() + endif() + unset( __gcc_toolchain ) + endforeach() +endmacro() + +# get all the details about NDK +if( BUILD_WITH_ANDROID_NDK ) + file( GLOB ANDROID_SUPPORTED_NATIVE_API_LEVELS RELATIVE "${ANDROID_NDK}/platforms" "${ANDROID_NDK}/platforms/android-*" ) + string( REPLACE "android-" "" ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_SUPPORTED_NATIVE_API_LEVELS}" ) + set( __availableToolchains "" ) + set( __availableToolchainMachines "" ) + set( __availableToolchainArchs "" ) + set( __availableToolchainCompilerVersions "" ) + if( ANDROID_TOOLCHAIN_NAME AND EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_TOOLCHAIN_NAME}/" ) + # do not go through all toolchains if we know the name + set( __availableToolchainsLst "${ANDROID_TOOLCHAIN_NAME}" ) + __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) + if( NOT __availableToolchains AND NOT ANDROID_NDK_TOOLCHAINS_SUBPATH STREQUAL ANDROID_NDK_TOOLCHAINS_SUBPATH2 ) + __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH2}" ) + if( __availableToolchains ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH ${ANDROID_NDK_TOOLCHAINS_SUBPATH2} ) + endif() + endif() + endif() + if( NOT __availableToolchains ) + file( GLOB __availableToolchainsLst RELATIVE "${ANDROID_NDK_TOOLCHAINS_PATH}" "${ANDROID_NDK_TOOLCHAINS_PATH}/*" ) + if( __availableToolchainsLst ) + list(SORT __availableToolchainsLst) # we need clang to go after gcc + endif() + __LIST_FILTER( __availableToolchainsLst "^[.]" ) + __LIST_FILTER( __availableToolchainsLst "llvm" ) + __LIST_FILTER( __availableToolchainsLst "renderscript" ) + __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) + if( NOT __availableToolchains AND NOT ANDROID_NDK_TOOLCHAINS_SUBPATH STREQUAL ANDROID_NDK_TOOLCHAINS_SUBPATH2 ) + __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH2}" ) + if( __availableToolchains ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH ${ANDROID_NDK_TOOLCHAINS_SUBPATH2} ) + endif() + endif() + endif() + if( NOT __availableToolchains ) + message( FATAL_ERROR "Could not find any working toolchain in the NDK. Probably your Android NDK is broken." ) + endif() +endif() + +# build list of available ABIs +set( ANDROID_SUPPORTED_ABIS "" ) +set( __uniqToolchainArchNames ${__availableToolchainArchs} ) +list( REMOVE_DUPLICATES __uniqToolchainArchNames ) +list( SORT __uniqToolchainArchNames ) +foreach( __arch ${__uniqToolchainArchNames} ) + list( APPEND ANDROID_SUPPORTED_ABIS ${ANDROID_SUPPORTED_ABIS_${__arch}} ) +endforeach() +unset( __uniqToolchainArchNames ) +if( NOT ANDROID_SUPPORTED_ABIS ) + message( FATAL_ERROR "No one of known Android ABIs is supported by this cmake toolchain." ) +endif() + +# choose target ABI +__INIT_VARIABLE( ANDROID_ABI VALUES ${ANDROID_SUPPORTED_ABIS} ) +# verify that target ABI is supported +list( FIND ANDROID_SUPPORTED_ABIS "${ANDROID_ABI}" __androidAbiIdx ) +if( __androidAbiIdx EQUAL -1 ) + string( REPLACE ";" "\", \"" PRINTABLE_ANDROID_SUPPORTED_ABIS "${ANDROID_SUPPORTED_ABIS}" ) + message( FATAL_ERROR "Specified ANDROID_ABI = \"${ANDROID_ABI}\" is not supported by this cmake toolchain or your NDK/toolchain. + Supported values are: \"${PRINTABLE_ANDROID_SUPPORTED_ABIS}\" + " ) +endif() +unset( __androidAbiIdx ) + +# set target ABI options +if( ANDROID_ABI STREQUAL "x86" ) + set( X86 true ) + set( ANDROID_NDK_ABI_NAME "x86" ) + set( ANDROID_ARCH_NAME "x86" ) + set( ANDROID_LLVM_TRIPLE "i686-none-linux-android" ) + set( CMAKE_SYSTEM_PROCESSOR "i686" ) +elseif( ANDROID_ABI STREQUAL "x86_64" ) + set( X86 true ) + set( X86_64 true ) + set( ANDROID_NDK_ABI_NAME "x86_64" ) + set( ANDROID_ARCH_NAME "x86_64" ) + set( CMAKE_SYSTEM_PROCESSOR "x86_64" ) + set( ANDROID_LLVM_TRIPLE "x86_64-none-linux-android" ) +elseif( ANDROID_ABI STREQUAL "mips64" ) + set( MIPS64 true ) + set( ANDROID_NDK_ABI_NAME "mips64" ) + set( ANDROID_ARCH_NAME "mips64" ) + set( ANDROID_LLVM_TRIPLE "mips64el-none-linux-android" ) + set( CMAKE_SYSTEM_PROCESSOR "mips64" ) +elseif( ANDROID_ABI STREQUAL "mips" ) + set( MIPS true ) + set( ANDROID_NDK_ABI_NAME "mips" ) + set( ANDROID_ARCH_NAME "mips" ) + set( ANDROID_LLVM_TRIPLE "mipsel-none-linux-android" ) + set( CMAKE_SYSTEM_PROCESSOR "mips" ) +elseif( ANDROID_ABI STREQUAL "arm64-v8a" ) + set( ARM64_V8A true ) + set( ANDROID_NDK_ABI_NAME "arm64-v8a" ) + set( ANDROID_ARCH_NAME "arm64" ) + set( ANDROID_LLVM_TRIPLE "aarch64-none-linux-android" ) + set( CMAKE_SYSTEM_PROCESSOR "aarch64" ) + set( VFPV3 true ) + set( NEON true ) +elseif( ANDROID_ABI STREQUAL "armeabi" ) + set( ARMEABI true ) + set( ANDROID_NDK_ABI_NAME "armeabi" ) + set( ANDROID_ARCH_NAME "arm" ) + set( ANDROID_LLVM_TRIPLE "armv5te-none-linux-androideabi" ) + set( CMAKE_SYSTEM_PROCESSOR "armv5te" ) +elseif( ANDROID_ABI STREQUAL "armeabi-v6 with VFP" ) + set( ARMEABI_V6 true ) + set( ANDROID_NDK_ABI_NAME "armeabi" ) + set( ANDROID_ARCH_NAME "arm" ) + set( ANDROID_LLVM_TRIPLE "armv5te-none-linux-androideabi" ) + set( CMAKE_SYSTEM_PROCESSOR "armv6" ) + # need always fallback to older platform + set( ARMEABI true ) +elseif( ANDROID_ABI STREQUAL "armeabi-v7a") + set( ARMEABI_V7A true ) + set( ANDROID_NDK_ABI_NAME "armeabi-v7a" ) + set( ANDROID_ARCH_NAME "arm" ) + set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" ) + set( CMAKE_SYSTEM_PROCESSOR "armv7-a" ) +elseif( ANDROID_ABI STREQUAL "armeabi-v7a with VFPV3" ) + set( ARMEABI_V7A true ) + set( ANDROID_NDK_ABI_NAME "armeabi-v7a" ) + set( ANDROID_ARCH_NAME "arm" ) + set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" ) + set( CMAKE_SYSTEM_PROCESSOR "armv7-a" ) + set( VFPV3 true ) +elseif( ANDROID_ABI STREQUAL "armeabi-v7a with NEON" ) + set( ARMEABI_V7A true ) + set( ANDROID_NDK_ABI_NAME "armeabi-v7a" ) + set( ANDROID_ARCH_NAME "arm" ) + set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" ) + set( CMAKE_SYSTEM_PROCESSOR "armv7-a" ) + set( VFPV3 true ) + set( NEON true ) +else() + message( SEND_ERROR "Unknown ANDROID_ABI=\"${ANDROID_ABI}\" is specified." ) +endif() + +if( CMAKE_BINARY_DIR AND EXISTS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeSystem.cmake" ) + # really dirty hack + # it is not possible to change CMAKE_SYSTEM_PROCESSOR after the first run... + file( APPEND "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeSystem.cmake" "SET(CMAKE_SYSTEM_PROCESSOR \"${CMAKE_SYSTEM_PROCESSOR}\")\n" ) +endif() + +if( ANDROID_ARCH_NAME STREQUAL "arm" AND NOT ARMEABI_V6 ) + __INIT_VARIABLE( ANDROID_FORCE_ARM_BUILD VALUES OFF ) + set( ANDROID_FORCE_ARM_BUILD ${ANDROID_FORCE_ARM_BUILD} CACHE BOOL "Use 32-bit ARM instructions instead of Thumb-1" FORCE ) + mark_as_advanced( ANDROID_FORCE_ARM_BUILD ) +else() + unset( ANDROID_FORCE_ARM_BUILD CACHE ) +endif() + +# choose toolchain +if( ANDROID_TOOLCHAIN_NAME ) + list( FIND __availableToolchains "${ANDROID_TOOLCHAIN_NAME}" __toolchainIdx ) + if( __toolchainIdx EQUAL -1 ) + list( SORT __availableToolchains ) + string( REPLACE ";" "\n * " toolchains_list "${__availableToolchains}" ) + set( toolchains_list " * ${toolchains_list}") + message( FATAL_ERROR "Specified toolchain \"${ANDROID_TOOLCHAIN_NAME}\" is missing in your NDK or broken. Please verify that your NDK is working or select another compiler toolchain. +To configure the toolchain set CMake variable ANDROID_TOOLCHAIN_NAME to one of the following values:\n${toolchains_list}\n" ) + endif() + list( GET __availableToolchainArchs ${__toolchainIdx} __toolchainArch ) + if( NOT __toolchainArch STREQUAL ANDROID_ARCH_NAME ) + message( SEND_ERROR "Selected toolchain \"${ANDROID_TOOLCHAIN_NAME}\" is not able to compile binaries for the \"${ANDROID_ARCH_NAME}\" platform." ) + endif() +else() + set( __toolchainIdx -1 ) + set( __applicableToolchains "" ) + set( __toolchainMaxVersion "0.0.0" ) + list( LENGTH __availableToolchains __availableToolchainsCount ) + math( EXPR __availableToolchainsCount "${__availableToolchainsCount}-1" ) + foreach( __idx RANGE ${__availableToolchainsCount} ) + list( GET __availableToolchainArchs ${__idx} __toolchainArch ) + if( __toolchainArch STREQUAL ANDROID_ARCH_NAME ) + list( GET __availableToolchainCompilerVersions ${__idx} __toolchainVersion ) + string( REPLACE "x" "99" __toolchainVersion "${__toolchainVersion}") + if( __toolchainVersion VERSION_GREATER __toolchainMaxVersion ) + set( __toolchainMaxVersion "${__toolchainVersion}" ) + set( __toolchainIdx ${__idx} ) + endif() + endif() + endforeach() + unset( __availableToolchainsCount ) + unset( __toolchainMaxVersion ) + unset( __toolchainVersion ) +endif() +unset( __toolchainArch ) +if( __toolchainIdx EQUAL -1 ) + message( FATAL_ERROR "No one of available compiler toolchains is able to compile for ${ANDROID_ARCH_NAME} platform." ) +endif() +list( GET __availableToolchains ${__toolchainIdx} ANDROID_TOOLCHAIN_NAME ) +list( GET __availableToolchainMachines ${__toolchainIdx} ANDROID_TOOLCHAIN_MACHINE_NAME ) +list( GET __availableToolchainCompilerVersions ${__toolchainIdx} ANDROID_COMPILER_VERSION ) + +unset( __toolchainIdx ) +unset( __availableToolchains ) +unset( __availableToolchainMachines ) +unset( __availableToolchainArchs ) +unset( __availableToolchainCompilerVersions ) + +# choose native API level +__INIT_VARIABLE( ANDROID_NATIVE_API_LEVEL ENV_ANDROID_NATIVE_API_LEVEL ANDROID_API_LEVEL ENV_ANDROID_API_LEVEL ANDROID_STANDALONE_TOOLCHAIN_API_LEVEL ANDROID_DEFAULT_NDK_API_LEVEL_${ANDROID_ARCH_NAME} ANDROID_DEFAULT_NDK_API_LEVEL ) +string( REPLACE "android-" "" ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" ) +string( STRIP "${ANDROID_NATIVE_API_LEVEL}" ANDROID_NATIVE_API_LEVEL ) +# adjust API level +set( __real_api_level ${ANDROID_DEFAULT_NDK_API_LEVEL_${ANDROID_ARCH_NAME}} ) +foreach( __level ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) + if( (__level LESS ANDROID_NATIVE_API_LEVEL OR __level STREQUAL ANDROID_NATIVE_API_LEVEL) AND NOT __level LESS __real_api_level ) + set( __real_api_level ${__level} ) + endif() +endforeach() +if( __real_api_level AND NOT ANDROID_NATIVE_API_LEVEL STREQUAL __real_api_level ) + message( STATUS "Adjusting Android API level 'android-${ANDROID_NATIVE_API_LEVEL}' to 'android-${__real_api_level}'") + set( ANDROID_NATIVE_API_LEVEL ${__real_api_level} ) +endif() +unset(__real_api_level) +# validate +list( FIND ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_NATIVE_API_LEVEL}" __levelIdx ) +if( __levelIdx EQUAL -1 ) + message( SEND_ERROR "Specified Android native API level 'android-${ANDROID_NATIVE_API_LEVEL}' is not supported by your NDK/toolchain." ) +else() + if( BUILD_WITH_ANDROID_NDK ) + __DETECT_NATIVE_API_LEVEL( __realApiLevel "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}/usr/include/android/api-level.h" ) + if( NOT __realApiLevel EQUAL ANDROID_NATIVE_API_LEVEL AND NOT __realApiLevel GREATER 9000 ) + message( SEND_ERROR "Specified Android API level (${ANDROID_NATIVE_API_LEVEL}) does not match to the level found (${__realApiLevel}). Probably your copy of NDK is broken." ) + endif() + unset( __realApiLevel ) + endif() + set( ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" CACHE STRING "Android API level for native code" FORCE ) + set( CMAKE_ANDROID_API ${ANDROID_NATIVE_API_LEVEL} ) + if( CMAKE_VERSION VERSION_GREATER "2.8" ) + list( SORT ANDROID_SUPPORTED_NATIVE_API_LEVELS ) + set_property( CACHE ANDROID_NATIVE_API_LEVEL PROPERTY STRINGS ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) + endif() +endif() +unset( __levelIdx ) + + +# remember target ABI +set( ANDROID_ABI "${ANDROID_ABI}" CACHE STRING "The target ABI for Android. If arm, then armeabi-v7a is recommended for hardware floating point." FORCE ) +if( CMAKE_VERSION VERSION_GREATER "2.8" ) + list( SORT ANDROID_SUPPORTED_ABIS_${ANDROID_ARCH_NAME} ) + set_property( CACHE ANDROID_ABI PROPERTY STRINGS ${ANDROID_SUPPORTED_ABIS_${ANDROID_ARCH_NAME}} ) +endif() + + +# runtime choice (STL, rtti, exceptions) +if( NOT ANDROID_STL ) + set( ANDROID_STL gnustl_static ) +endif() +set( ANDROID_STL "${ANDROID_STL}" CACHE STRING "C++ runtime" ) +set( ANDROID_STL_FORCE_FEATURES ON CACHE BOOL "automatically configure rtti and exceptions support based on C++ runtime" ) +mark_as_advanced( ANDROID_STL ANDROID_STL_FORCE_FEATURES ) + +if( BUILD_WITH_ANDROID_NDK ) + if( NOT "${ANDROID_STL}" MATCHES "^(none|system|system_re|gabi\\+\\+_static|gabi\\+\\+_shared|stlport_static|stlport_shared|gnustl_static|gnustl_shared)$") + message( FATAL_ERROR "ANDROID_STL is set to invalid value \"${ANDROID_STL}\". +The possible values are: + none -> Do not configure the runtime. + system -> Use the default minimal system C++ runtime library. + system_re -> Same as system but with rtti and exceptions. + gabi++_static -> Use the GAbi++ runtime as a static library. + gabi++_shared -> Use the GAbi++ runtime as a shared library. + stlport_static -> Use the STLport runtime as a static library. + stlport_shared -> Use the STLport runtime as a shared library. + gnustl_static -> (default) Use the GNU STL as a static library. + gnustl_shared -> Use the GNU STL as a shared library. +" ) + endif() +elseif( BUILD_WITH_STANDALONE_TOOLCHAIN ) + if( NOT "${ANDROID_STL}" MATCHES "^(none|gnustl_static|gnustl_shared)$") + message( FATAL_ERROR "ANDROID_STL is set to invalid value \"${ANDROID_STL}\". +The possible values are: + none -> Do not configure the runtime. + gnustl_static -> (default) Use the GNU STL as a static library. + gnustl_shared -> Use the GNU STL as a shared library. +" ) + endif() +endif() + +unset( ANDROID_RTTI ) +unset( ANDROID_EXCEPTIONS ) +unset( ANDROID_STL_INCLUDE_DIRS ) +unset( __libstl ) +unset( __libsupcxx ) + +if( NOT _CMAKE_IN_TRY_COMPILE AND ANDROID_NDK_RELEASE STREQUAL "r7b" AND ARMEABI_V7A AND NOT VFPV3 AND ANDROID_STL MATCHES "gnustl" ) + message( WARNING "The GNU STL armeabi-v7a binaries from NDK r7b can crash non-NEON devices. The files provided with NDK r7b were not configured properly, resulting in crashes on Tegra2-based devices and others when trying to use certain floating-point functions (e.g., cosf, sinf, expf). +You are strongly recommended to switch to another NDK release. +" ) +endif() + +if( NOT _CMAKE_IN_TRY_COMPILE AND X86 AND ANDROID_STL MATCHES "gnustl" AND ANDROID_NDK_RELEASE STREQUAL "r6" ) + message( WARNING "The x86 system header file from NDK r6 has incorrect definition for ptrdiff_t. You are recommended to upgrade to a newer NDK release or manually patch the header: +See https://android.googlesource.com/platform/development.git f907f4f9d4e56ccc8093df6fee54454b8bcab6c2 + diff --git a/ndk/platforms/android-9/arch-x86/include/machine/_types.h b/ndk/platforms/android-9/arch-x86/include/machine/_types.h + index 5e28c64..65892a1 100644 + --- a/ndk/platforms/android-9/arch-x86/include/machine/_types.h + +++ b/ndk/platforms/android-9/arch-x86/include/machine/_types.h + @@ -51,7 +51,11 @@ typedef long int ssize_t; + #endif + #ifndef _PTRDIFF_T + #define _PTRDIFF_T + -typedef long ptrdiff_t; + +# ifdef __ANDROID__ + + typedef int ptrdiff_t; + +# else + + typedef long ptrdiff_t; + +# endif + #endif +" ) +endif() + + +# setup paths and STL for standalone toolchain +if( BUILD_WITH_STANDALONE_TOOLCHAIN ) + set( ANDROID_TOOLCHAIN_ROOT "${ANDROID_STANDALONE_TOOLCHAIN}" ) + set( ANDROID_CLANG_TOOLCHAIN_ROOT "${ANDROID_STANDALONE_TOOLCHAIN}" ) + set( ANDROID_SYSROOT "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot" ) + + if( NOT ANDROID_STL STREQUAL "none" ) + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_STANDALONE_TOOLCHAIN}/include/c++/${ANDROID_COMPILER_VERSION}" ) + if( NOT EXISTS "${ANDROID_STL_INCLUDE_DIRS}" ) + # old location ( pre r8c ) + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/include/c++/${ANDROID_COMPILER_VERSION}" ) + endif() + if( ARMEABI_V7A AND EXISTS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}/bits" ) + list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}" ) + elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/thumb/bits" ) + list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/thumb" ) + else() + list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}" ) + endif() + # always search static GNU STL to get the location of libsupc++.a + if( ARMEABI_V7A AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb/libstdc++.a" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb" ) + elseif( ARMEABI_V7A AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libstdc++.a" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}" ) + elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libstdc++.a" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb" ) + elseif( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libstdc++.a" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib" ) + endif() + if( __libstl ) + set( __libsupcxx "${__libstl}/libsupc++.a" ) + set( __libstl "${__libstl}/libstdc++.a" ) + endif() + if( NOT EXISTS "${__libsupcxx}" ) + message( FATAL_ERROR "The required libstdsupc++.a is missing in your standalone toolchain. + Usually it happens because of bug in make-standalone-toolchain.sh script from NDK r7, r7b and r7c. + You need to either upgrade to newer NDK or manually copy + $ANDROID_NDK/sources/cxx-stl/gnu-libstdc++/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a + to + ${__libsupcxx} + " ) + endif() + if( ANDROID_STL STREQUAL "gnustl_shared" ) + if( ARMEABI_V7A AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libgnustl_shared.so" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libgnustl_shared.so" ) + elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libgnustl_shared.so" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libgnustl_shared.so" ) + elseif( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libgnustl_shared.so" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libgnustl_shared.so" ) + endif() + endif() + endif() +endif() + +# clang +if( "${ANDROID_TOOLCHAIN_NAME}" STREQUAL "standalone-clang" ) + set( ANDROID_COMPILER_IS_CLANG 1 ) + execute_process( COMMAND "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/clang${TOOL_OS_SUFFIX}" --version OUTPUT_VARIABLE ANDROID_CLANG_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ) + string( REGEX MATCH "[0-9]+[.][0-9]+" ANDROID_CLANG_VERSION "${ANDROID_CLANG_VERSION}") +elseif( "${ANDROID_TOOLCHAIN_NAME}" MATCHES "-clang3[.][0-9]?$" ) + string( REGEX MATCH "3[.][0-9]$" ANDROID_CLANG_VERSION "${ANDROID_TOOLCHAIN_NAME}") + string( REGEX REPLACE "-clang${ANDROID_CLANG_VERSION}$" "-${ANDROID_COMPILER_VERSION}" ANDROID_GCC_TOOLCHAIN_NAME "${ANDROID_TOOLCHAIN_NAME}" ) + if( NOT EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/llvm-${ANDROID_CLANG_VERSION}${ANDROID_NDK_TOOLCHAINS_SUBPATH}/bin/clang${TOOL_OS_SUFFIX}" ) + message( FATAL_ERROR "Could not find the Clang compiler driver" ) + endif() + set( ANDROID_COMPILER_IS_CLANG 1 ) + set( ANDROID_CLANG_TOOLCHAIN_ROOT "${ANDROID_NDK_TOOLCHAINS_PATH}/llvm-${ANDROID_CLANG_VERSION}${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) +else() + set( ANDROID_GCC_TOOLCHAIN_NAME "${ANDROID_TOOLCHAIN_NAME}" ) + unset( ANDROID_COMPILER_IS_CLANG CACHE ) +endif() + +string( REPLACE "." "" _clang_name "clang${ANDROID_CLANG_VERSION}" ) +if( NOT EXISTS "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" ) + set( _clang_name "clang" ) +endif() + + +# setup paths and STL for NDK +if( BUILD_WITH_ANDROID_NDK ) + set( ANDROID_TOOLCHAIN_ROOT "${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) + set( ANDROID_SYSROOT "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}" ) + + if( ANDROID_STL STREQUAL "none" ) + # do nothing + elseif( ANDROID_STL STREQUAL "system" ) + set( ANDROID_RTTI OFF ) + set( ANDROID_EXCEPTIONS OFF ) + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/system/include" ) + elseif( ANDROID_STL STREQUAL "system_re" ) + set( ANDROID_RTTI ON ) + set( ANDROID_EXCEPTIONS ON ) + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/system/include" ) + elseif( ANDROID_STL MATCHES "gabi" ) + if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7 + message( FATAL_ERROR "gabi++ is not available in your NDK. You have to upgrade to NDK r7 or newer to use gabi++.") + endif() + set( ANDROID_RTTI ON ) + set( ANDROID_EXCEPTIONS OFF ) + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/gabi++/include" ) + set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gabi++/libs/${ANDROID_NDK_ABI_NAME}/libgabi++_static.a" ) + elseif( ANDROID_STL MATCHES "stlport" ) + if( NOT ANDROID_NDK_RELEASE_NUM LESS 8004 ) # before r8d + set( ANDROID_EXCEPTIONS ON ) + else() + set( ANDROID_EXCEPTIONS OFF ) + endif() + if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7 + set( ANDROID_RTTI OFF ) + else() + set( ANDROID_RTTI ON ) + endif() + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/stlport/stlport" ) + set( __libstl "${ANDROID_NDK}/sources/cxx-stl/stlport/libs/${ANDROID_NDK_ABI_NAME}/libstlport_static.a" ) + elseif( ANDROID_STL MATCHES "gnustl" ) + set( ANDROID_EXCEPTIONS ON ) + set( ANDROID_RTTI ON ) + if( EXISTS "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}" ) + if( ARMEABI_V7A AND ANDROID_COMPILER_VERSION VERSION_EQUAL "4.7" AND ANDROID_NDK_RELEASE STREQUAL "r8d" ) + # gnustl binary for 4.7 compiler is buggy :( + # TODO: look for right fix + set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.6" ) + else() + set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}" ) + endif() + else() + set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++" ) + endif() + set( ANDROID_STL_INCLUDE_DIRS "${__libstl}/include" "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/include" "${__libstl}/include/backward" ) + if( EXISTS "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libgnustl_static.a" ) + set( __libstl "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libgnustl_static.a" ) + else() + set( __libstl "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libstdc++.a" ) + endif() + else() + message( FATAL_ERROR "Unknown runtime: ${ANDROID_STL}" ) + endif() + # find libsupc++.a - rtti & exceptions + if( ANDROID_STL STREQUAL "system_re" OR ANDROID_STL MATCHES "gnustl" ) + set( __libsupcxx "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a" ) # r8b or newer + if( NOT EXISTS "${__libsupcxx}" ) + set( __libsupcxx "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a" ) # r7-r8 + endif() + if( NOT EXISTS "${__libsupcxx}" ) # before r7 + if( ARMEABI_V7A ) + if( ANDROID_FORCE_ARM_BUILD ) + set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libsupc++.a" ) + else() + set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb/libsupc++.a" ) + endif() + elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD ) + set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libsupc++.a" ) + else() + set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libsupc++.a" ) + endif() + endif() + if( NOT EXISTS "${__libsupcxx}") + message( ERROR "Could not find libsupc++.a for a chosen platform. Either your NDK is not supported or is broken.") + endif() + endif() +endif() + + +# case of shared STL linkage +if( ANDROID_STL MATCHES "shared" AND DEFINED __libstl ) + string( REPLACE "_static.a" "_shared.so" __libstl "${__libstl}" ) + # TODO: check if .so file exists before the renaming +endif() + + +# ccache support +__INIT_VARIABLE( _ndk_ccache NDK_CCACHE ENV_NDK_CCACHE ) +if( _ndk_ccache ) + if( DEFINED NDK_CCACHE AND NOT EXISTS NDK_CCACHE ) + unset( NDK_CCACHE CACHE ) + endif() + find_program( NDK_CCACHE "${_ndk_ccache}" DOC "The path to ccache binary") +else() + unset( NDK_CCACHE CACHE ) +endif() +unset( _ndk_ccache ) + + +# setup the cross-compiler +if( NOT CMAKE_C_COMPILER ) + if( NDK_CCACHE AND NOT ANDROID_SYSROOT MATCHES "[ ;\"]" ) + set( CMAKE_C_COMPILER "${NDK_CCACHE}" CACHE PATH "ccache as C compiler" ) + set( CMAKE_CXX_COMPILER "${NDK_CCACHE}" CACHE PATH "ccache as C++ compiler" ) + if( ANDROID_COMPILER_IS_CLANG ) + set( CMAKE_C_COMPILER_ARG1 "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" CACHE PATH "C compiler") + set( CMAKE_CXX_COMPILER_ARG1 "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler") + else() + set( CMAKE_C_COMPILER_ARG1 "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "C compiler") + set( CMAKE_CXX_COMPILER_ARG1 "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-g++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler") + endif() + else() + if( ANDROID_COMPILER_IS_CLANG ) + set( CMAKE_C_COMPILER "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" CACHE PATH "C compiler") + set( CMAKE_CXX_COMPILER "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler") + else() + set( CMAKE_C_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "C compiler" ) + set( CMAKE_CXX_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-g++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler" ) + endif() + endif() + set( CMAKE_ASM_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "assembler" ) + set( CMAKE_STRIP "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-strip${TOOL_OS_SUFFIX}" CACHE PATH "strip" ) + if( EXISTS "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc-ar${TOOL_OS_SUFFIX}" ) + # Use gcc-ar if we have it for better LTO support. + set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" ) + else() + set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" ) + endif() + set( CMAKE_LINKER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ld${TOOL_OS_SUFFIX}" CACHE PATH "linker" ) + set( CMAKE_NM "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-nm${TOOL_OS_SUFFIX}" CACHE PATH "nm" ) + set( CMAKE_OBJCOPY "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objcopy${TOOL_OS_SUFFIX}" CACHE PATH "objcopy" ) + set( CMAKE_OBJDUMP "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objdump${TOOL_OS_SUFFIX}" CACHE PATH "objdump" ) + set( CMAKE_RANLIB "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ranlib${TOOL_OS_SUFFIX}" CACHE PATH "ranlib" ) +endif() + +set( _CMAKE_TOOLCHAIN_PREFIX "${ANDROID_TOOLCHAIN_MACHINE_NAME}-" ) +if( CMAKE_VERSION VERSION_LESS 2.8.5 ) + set( CMAKE_ASM_COMPILER_ARG1 "-c" ) +endif() +if( APPLE ) + find_program( CMAKE_INSTALL_NAME_TOOL NAMES install_name_tool ) + if( NOT CMAKE_INSTALL_NAME_TOOL ) + message( FATAL_ERROR "Could not find install_name_tool, please check your installation." ) + endif() + mark_as_advanced( CMAKE_INSTALL_NAME_TOOL ) +endif() + +# Force set compilers because standard identification works badly for us +include( CMakeForceCompiler ) +CMAKE_FORCE_C_COMPILER( "${CMAKE_C_COMPILER}" GNU ) +if( ANDROID_COMPILER_IS_CLANG ) + set( CMAKE_C_COMPILER_ID Clang ) +endif() +set( CMAKE_C_PLATFORM_ID Linux ) +if( X86_64 OR MIPS64 OR ARM64_V8A ) + set( CMAKE_C_SIZEOF_DATA_PTR 8 ) +else() + set( CMAKE_C_SIZEOF_DATA_PTR 4 ) +endif() +set( CMAKE_C_HAS_ISYSROOT 1 ) +set( CMAKE_C_COMPILER_ABI ELF ) +CMAKE_FORCE_CXX_COMPILER( "${CMAKE_CXX_COMPILER}" GNU ) +if( ANDROID_COMPILER_IS_CLANG ) + set( CMAKE_CXX_COMPILER_ID Clang) +endif() +set( CMAKE_CXX_PLATFORM_ID Linux ) +set( CMAKE_CXX_SIZEOF_DATA_PTR ${CMAKE_C_SIZEOF_DATA_PTR} ) +set( CMAKE_CXX_HAS_ISYSROOT 1 ) +set( CMAKE_CXX_COMPILER_ABI ELF ) +set( CMAKE_CXX_SOURCE_FILE_EXTENSIONS cc cp cxx cpp CPP c++ C ) +# force ASM compiler (required for CMake < 2.8.5) +set( CMAKE_ASM_COMPILER_ID_RUN TRUE ) +set( CMAKE_ASM_COMPILER_ID GNU ) +set( CMAKE_ASM_COMPILER_WORKS TRUE ) +set( CMAKE_ASM_COMPILER_FORCED TRUE ) +set( CMAKE_COMPILER_IS_GNUASM 1) +set( CMAKE_ASM_SOURCE_FILE_EXTENSIONS s S asm ) + +foreach( lang C CXX ASM ) + if( ANDROID_COMPILER_IS_CLANG ) + set( CMAKE_${lang}_COMPILER_VERSION ${ANDROID_CLANG_VERSION} ) + else() + set( CMAKE_${lang}_COMPILER_VERSION ${ANDROID_COMPILER_VERSION} ) + endif() +endforeach() + +# flags and definitions +remove_definitions( -DANDROID ) +add_definitions( -DANDROID ) + +if( ANDROID_SYSROOT MATCHES "[ ;\"]" ) + if( CMAKE_HOST_WIN32 ) + # try to convert path to 8.3 form + file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cvt83.cmd" "@echo %~s1" ) + execute_process( COMMAND "$ENV{ComSpec}" /c "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cvt83.cmd" "${ANDROID_SYSROOT}" + OUTPUT_VARIABLE __path OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE __result ERROR_QUIET ) + if( __result EQUAL 0 ) + file( TO_CMAKE_PATH "${__path}" ANDROID_SYSROOT ) + set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT}" ) + else() + set( ANDROID_CXX_FLAGS "--sysroot=\"${ANDROID_SYSROOT}\"" ) + endif() + else() + set( ANDROID_CXX_FLAGS "'--sysroot=${ANDROID_SYSROOT}'" ) + endif() + if( NOT _CMAKE_IN_TRY_COMPILE ) + # quotes can break try_compile and compiler identification + message(WARNING "Path to your Android NDK (or toolchain) has non-alphanumeric symbols.\nThe build might be broken.\n") + endif() +else() + set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT}" ) +endif() + +# NDK flags +if (ARM64_V8A ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" ) + set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer -fstrict-aliasing" ) + set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer -fno-strict-aliasing" ) + if( NOT ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} -funswitch-loops -finline-limit=300" ) + endif() +elseif( ARMEABI OR ARMEABI_V7A) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" ) + if( NOT ANDROID_FORCE_ARM_BUILD AND NOT ARMEABI_V6 ) + set( ANDROID_CXX_FLAGS_RELEASE "-mthumb -fomit-frame-pointer -fno-strict-aliasing" ) + set( ANDROID_CXX_FLAGS_DEBUG "-marm -fno-omit-frame-pointer -fno-strict-aliasing" ) + if( NOT ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -finline-limit=64" ) + endif() + else() + # always compile ARMEABI_V6 in arm mode; otherwise there is no difference from ARMEABI + set( ANDROID_CXX_FLAGS_RELEASE "-marm -fomit-frame-pointer -fstrict-aliasing" ) + set( ANDROID_CXX_FLAGS_DEBUG "-marm -fno-omit-frame-pointer -fno-strict-aliasing" ) + if( NOT ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funswitch-loops -finline-limit=300" ) + endif() + endif() +elseif( X86 OR X86_64 ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" ) + if( NOT ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funswitch-loops -finline-limit=300" ) + endif() + set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer -fstrict-aliasing" ) + set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer -fno-strict-aliasing" ) +elseif( MIPS OR MIPS64 ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fno-strict-aliasing -finline-functions -funwind-tables -fmessage-length=0" ) + set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer" ) + set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer" ) + if( NOT ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers" ) + set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} -funswitch-loops -finline-limit=300" ) + endif() +elseif() + set( ANDROID_CXX_FLAGS_RELEASE "" ) + set( ANDROID_CXX_FLAGS_DEBUG "" ) +endif() + +set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fsigned-char" ) # good/necessary when porting desktop libraries + +if( NOT X86 AND NOT ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "-Wno-psabi ${ANDROID_CXX_FLAGS}" ) +endif() + +if( NOT ANDROID_COMPILER_VERSION VERSION_LESS "4.6" ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -no-canonical-prefixes" ) # see https://android-review.googlesource.com/#/c/47564/ +endif() + +# ABI-specific flags +if( ARMEABI_V7A ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv7-a -mfloat-abi=softfp" ) + if( NEON ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=neon" ) + elseif( VFPV3 ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=vfpv3" ) + else() + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=vfpv3-d16" ) + endif() +elseif( ARMEABI_V6 ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv6 -mfloat-abi=softfp -mfpu=vfp" ) # vfp == vfpv2 +elseif( ARMEABI ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv5te -mtune=xscale -msoft-float" ) +endif() + +if( ANDROID_STL MATCHES "gnustl" AND (EXISTS "${__libstl}" OR EXISTS "${__libsupcxx}") ) + set( CMAKE_CXX_CREATE_SHARED_LIBRARY " -o " ) + set( CMAKE_CXX_CREATE_SHARED_MODULE " -o " ) + set( CMAKE_CXX_LINK_EXECUTABLE " -o " ) +else() + set( CMAKE_CXX_CREATE_SHARED_LIBRARY " -o " ) + set( CMAKE_CXX_CREATE_SHARED_MODULE " -o " ) + set( CMAKE_CXX_LINK_EXECUTABLE " -o " ) +endif() + +# STL +if( EXISTS "${__libstl}" OR EXISTS "${__libsupcxx}" ) + if( EXISTS "${__libstl}" ) + set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libstl}\"" ) + set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libstl}\"" ) + set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libstl}\"" ) + endif() + if( EXISTS "${__libsupcxx}" ) + set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"" ) + set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libsupcxx}\"" ) + set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libsupcxx}\"" ) + # C objects: + set( CMAKE_C_CREATE_SHARED_LIBRARY " -o " ) + set( CMAKE_C_CREATE_SHARED_MODULE " -o " ) + set( CMAKE_C_LINK_EXECUTABLE " -o " ) + set( CMAKE_C_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"" ) + set( CMAKE_C_CREATE_SHARED_MODULE "${CMAKE_C_CREATE_SHARED_MODULE} \"${__libsupcxx}\"" ) + set( CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINK_EXECUTABLE} \"${__libsupcxx}\"" ) + endif() + if( ANDROID_STL MATCHES "gnustl" ) + if( NOT EXISTS "${ANDROID_LIBM_PATH}" ) + set( ANDROID_LIBM_PATH -lm ) + endif() + set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} ${ANDROID_LIBM_PATH}" ) + set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} ${ANDROID_LIBM_PATH}" ) + set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} ${ANDROID_LIBM_PATH}" ) + endif() +endif() + +# variables controlling optional build flags +if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7 + # libGLESv2.so in NDK's prior to r7 refers to missing external symbols. + # So this flag option is required for all projects using OpenGL from native. + __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES ON ) +else() + __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES OFF ) +endif() +__INIT_VARIABLE( ANDROID_NO_UNDEFINED VALUES ON ) +__INIT_VARIABLE( ANDROID_FUNCTION_LEVEL_LINKING VALUES ON ) +__INIT_VARIABLE( ANDROID_GOLD_LINKER VALUES ON ) +__INIT_VARIABLE( ANDROID_NOEXECSTACK VALUES ON ) +__INIT_VARIABLE( ANDROID_RELRO VALUES ON ) + +set( ANDROID_NO_UNDEFINED ${ANDROID_NO_UNDEFINED} CACHE BOOL "Show all undefined symbols as linker errors" ) +set( ANDROID_SO_UNDEFINED ${ANDROID_SO_UNDEFINED} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) +set( ANDROID_FUNCTION_LEVEL_LINKING ${ANDROID_FUNCTION_LEVEL_LINKING} CACHE BOOL "Put each function in separate section and enable garbage collection of unused input sections at link time" ) +set( ANDROID_GOLD_LINKER ${ANDROID_GOLD_LINKER} CACHE BOOL "Enables gold linker" ) +set( ANDROID_NOEXECSTACK ${ANDROID_NOEXECSTACK} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) +set( ANDROID_RELRO ${ANDROID_RELRO} CACHE BOOL "Enables RELRO - a memory corruption mitigation technique" ) +mark_as_advanced( ANDROID_NO_UNDEFINED ANDROID_SO_UNDEFINED ANDROID_FUNCTION_LEVEL_LINKING ANDROID_GOLD_LINKER ANDROID_NOEXECSTACK ANDROID_RELRO ) + +# linker flags +set( ANDROID_LINKER_FLAGS "" ) + +if( ARMEABI_V7A ) + # this is *required* to use the following linker flags that routes around + # a CPU bug in some Cortex-A8 implementations: + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--fix-cortex-a8" ) +endif() + +if( ANDROID_NO_UNDEFINED ) + if( MIPS ) + # there is some sysroot-related problem in mips linker... + if( NOT ANDROID_SYSROOT MATCHES "[ ;\"]" ) + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--no-undefined -Wl,-rpath-link,${ANDROID_SYSROOT}/usr/lib" ) + endif() + else() + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--no-undefined" ) + endif() +endif() + +if( ANDROID_SO_UNDEFINED ) + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-allow-shlib-undefined" ) +endif() + +if( ANDROID_FUNCTION_LEVEL_LINKING ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fdata-sections -ffunction-sections" ) + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--gc-sections" ) +endif() + +if( ANDROID_COMPILER_VERSION VERSION_EQUAL "4.6" ) + if( ANDROID_GOLD_LINKER AND (CMAKE_HOST_UNIX OR ANDROID_NDK_RELEASE_NUM GREATER 8002) AND (ARMEABI OR ARMEABI_V7A OR X86) ) + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=gold" ) + elseif( ANDROID_NDK_RELEASE_NUM GREATER 8002 ) # after r8b + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=bfd" ) + elseif( ANDROID_NDK_RELEASE STREQUAL "r8b" AND ARMEABI AND NOT _CMAKE_IN_TRY_COMPILE ) + message( WARNING "The default bfd linker from arm GCC 4.6 toolchain can fail with 'unresolvable R_ARM_THM_CALL relocation' error message. See https://code.google.com/p/android/issues/detail?id=35342 + On Linux and OS X host platform you can workaround this problem using gold linker (default). + Rerun cmake with -DANDROID_GOLD_LINKER=ON option in case of problems. +" ) + endif() +endif() # version 4.6 + +if( ANDROID_NOEXECSTACK ) + if( ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -Xclang -mnoexecstack" ) + else() + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -Wa,--noexecstack" ) + endif() + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-z,noexecstack" ) +endif() + +if( ANDROID_RELRO ) + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now" ) +endif() + +if( ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "-target ${ANDROID_LLVM_TRIPLE} -Qunused-arguments ${ANDROID_CXX_FLAGS}" ) + if( BUILD_WITH_ANDROID_NDK ) + set( ANDROID_CXX_FLAGS "-gcc-toolchain ${ANDROID_TOOLCHAIN_ROOT} ${ANDROID_CXX_FLAGS}" ) + endif() +endif() + +# cache flags +set( CMAKE_CXX_FLAGS "" CACHE STRING "c++ flags" ) +set( CMAKE_C_FLAGS "" CACHE STRING "c flags" ) +set( CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG" CACHE STRING "c++ Release flags" ) +set( CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG" CACHE STRING "c Release flags" ) +set( CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DDEBUG -D_DEBUG" CACHE STRING "c++ Debug flags" ) +set( CMAKE_C_FLAGS_DEBUG "-O0 -g -DDEBUG -D_DEBUG" CACHE STRING "c Debug flags" ) +set( CMAKE_SHARED_LINKER_FLAGS "" CACHE STRING "shared linker flags" ) +set( CMAKE_MODULE_LINKER_FLAGS "" CACHE STRING "module linker flags" ) +set( CMAKE_EXE_LINKER_FLAGS "-Wl,-z,nocopyreloc" CACHE STRING "executable linker flags" ) + +# put flags to cache (for debug purpose only) +set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS}" CACHE INTERNAL "Android specific c/c++ flags" ) +set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE}" CACHE INTERNAL "Android specific c/c++ Release flags" ) +set( ANDROID_CXX_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG}" CACHE INTERNAL "Android specific c/c++ Debug flags" ) +set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS}" CACHE INTERNAL "Android specific c/c++ linker flags" ) + +# finish flags +set( CMAKE_CXX_FLAGS "${ANDROID_CXX_FLAGS} ${CMAKE_CXX_FLAGS}" ) +set( CMAKE_C_FLAGS "${ANDROID_CXX_FLAGS} ${CMAKE_C_FLAGS}" ) +set( CMAKE_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_RELEASE}" ) +set( CMAKE_C_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} ${CMAKE_C_FLAGS_RELEASE}" ) +set( CMAKE_CXX_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG} ${CMAKE_CXX_FLAGS_DEBUG}" ) +set( CMAKE_C_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG} ${CMAKE_C_FLAGS_DEBUG}" ) +set( CMAKE_SHARED_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}" ) +set( CMAKE_MODULE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}" ) +set( CMAKE_EXE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}" ) + +if( MIPS AND BUILD_WITH_ANDROID_NDK AND ANDROID_NDK_RELEASE STREQUAL "r8" ) + set( CMAKE_SHARED_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.xsc ${CMAKE_SHARED_LINKER_FLAGS}" ) + set( CMAKE_MODULE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.xsc ${CMAKE_MODULE_LINKER_FLAGS}" ) + set( CMAKE_EXE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.x ${CMAKE_EXE_LINKER_FLAGS}" ) +endif() + +# pie/pic +if( NOT (ANDROID_NATIVE_API_LEVEL LESS 16) AND (NOT DEFINED ANDROID_APP_PIE OR ANDROID_APP_PIE) AND (CMAKE_VERSION VERSION_GREATER 2.8.8) ) + set( CMAKE_POSITION_INDEPENDENT_CODE TRUE ) + set( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fPIE -pie") +else() + set( CMAKE_POSITION_INDEPENDENT_CODE FALSE ) + set( CMAKE_CXX_FLAGS "-fpic ${CMAKE_CXX_FLAGS}" ) + set( CMAKE_C_FLAGS "-fpic ${CMAKE_C_FLAGS}" ) +endif() + +# configure rtti +if( DEFINED ANDROID_RTTI AND ANDROID_STL_FORCE_FEATURES ) + if( ANDROID_RTTI ) + set( CMAKE_CXX_FLAGS "-frtti ${CMAKE_CXX_FLAGS}" ) + else() + set( CMAKE_CXX_FLAGS "-fno-rtti ${CMAKE_CXX_FLAGS}" ) + endif() +endif() + +# configure exceptios +if( DEFINED ANDROID_EXCEPTIONS AND ANDROID_STL_FORCE_FEATURES ) + if( ANDROID_EXCEPTIONS ) + set( CMAKE_CXX_FLAGS "-fexceptions ${CMAKE_CXX_FLAGS}" ) + set( CMAKE_C_FLAGS "-fexceptions ${CMAKE_C_FLAGS}" ) + else() + set( CMAKE_CXX_FLAGS "-fno-exceptions ${CMAKE_CXX_FLAGS}" ) + set( CMAKE_C_FLAGS "-fno-exceptions ${CMAKE_C_FLAGS}" ) + endif() +endif() + +# global includes and link directories +include_directories( SYSTEM "${ANDROID_SYSROOT}/usr/include" ${ANDROID_STL_INCLUDE_DIRS} ) +get_filename_component(__android_install_path "${CMAKE_INSTALL_PREFIX}/libs/${ANDROID_NDK_ABI_NAME}" ABSOLUTE) # avoid CMP0015 policy warning +link_directories( "${__android_install_path}" ) + +# detect if need link crtbegin_so.o explicitly +if( NOT DEFINED ANDROID_EXPLICIT_CRT_LINK ) + set( __cmd "${CMAKE_CXX_CREATE_SHARED_LIBRARY}" ) + string( REPLACE "" "${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1}" __cmd "${__cmd}" ) + string( REPLACE "" "${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}" __cmd "${__cmd}" ) + string( REPLACE "" "${CMAKE_CXX_FLAGS}" __cmd "${__cmd}" ) + string( REPLACE "" "" __cmd "${__cmd}" ) + string( REPLACE "" "${CMAKE_SHARED_LINKER_FLAGS}" __cmd "${__cmd}" ) + string( REPLACE "" "-shared" __cmd "${__cmd}" ) + string( REPLACE "" "" __cmd "${__cmd}" ) + string( REPLACE "" "" __cmd "${__cmd}" ) + string( REPLACE "" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/toolchain_crtlink_test.so" __cmd "${__cmd}" ) + string( REPLACE "" "\"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" __cmd "${__cmd}" ) + string( REPLACE "" "" __cmd "${__cmd}" ) + separate_arguments( __cmd ) + foreach( __var ANDROID_NDK ANDROID_NDK_TOOLCHAINS_PATH ANDROID_STANDALONE_TOOLCHAIN ) + if( ${__var} ) + set( __tmp "${${__var}}" ) + separate_arguments( __tmp ) + string( REPLACE "${__tmp}" "${${__var}}" __cmd "${__cmd}") + endif() + endforeach() + string( REPLACE "'" "" __cmd "${__cmd}" ) + string( REPLACE "\"" "" __cmd "${__cmd}" ) + execute_process( COMMAND ${__cmd} RESULT_VARIABLE __cmd_result OUTPUT_QUIET ERROR_QUIET ) + if( __cmd_result EQUAL 0 ) + set( ANDROID_EXPLICIT_CRT_LINK ON ) + else() + set( ANDROID_EXPLICIT_CRT_LINK OFF ) + endif() +endif() + +if( ANDROID_EXPLICIT_CRT_LINK ) + set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" ) + set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" ) +endif() + +# setup output directories +set( CMAKE_INSTALL_PREFIX "${ANDROID_TOOLCHAIN_ROOT}/user" CACHE STRING "path for installing" ) + +if( DEFINED LIBRARY_OUTPUT_PATH_ROOT + OR EXISTS "${CMAKE_SOURCE_DIR}/AndroidManifest.xml" + OR (EXISTS "${CMAKE_SOURCE_DIR}/../AndroidManifest.xml" AND EXISTS "${CMAKE_SOURCE_DIR}/../jni/") ) + set( LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_SOURCE_DIR} CACHE PATH "Root for binaries output, set this to change where Android libs are installed to" ) + if( NOT _CMAKE_IN_TRY_COMPILE ) + if( EXISTS "${CMAKE_SOURCE_DIR}/jni/CMakeLists.txt" ) + set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for applications" ) + else() + set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin" CACHE PATH "Output directory for applications" ) + endif() + set( LIBRARY_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for Android libs" ) + endif() +endif() + +# copy shaed stl library to build directory +if( NOT _CMAKE_IN_TRY_COMPILE AND __libstl MATCHES "[.]so$" AND DEFINED LIBRARY_OUTPUT_PATH ) + get_filename_component( __libstlname "${__libstl}" NAME ) + execute_process( COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${__libstl}" "${LIBRARY_OUTPUT_PATH}/${__libstlname}" RESULT_VARIABLE __fileCopyProcess ) + if( NOT __fileCopyProcess EQUAL 0 OR NOT EXISTS "${LIBRARY_OUTPUT_PATH}/${__libstlname}") + message( SEND_ERROR "Failed copying of ${__libstl} to the ${LIBRARY_OUTPUT_PATH}/${__libstlname}" ) + endif() + unset( __fileCopyProcess ) + unset( __libstlname ) +endif() + + +# set these global flags for cmake client scripts to change behavior +set( ANDROID True ) +set( BUILD_ANDROID True ) + +# where is the target environment +set( CMAKE_FIND_ROOT_PATH "${ANDROID_TOOLCHAIN_ROOT}/bin" "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}" "${ANDROID_SYSROOT}" "${CMAKE_INSTALL_PREFIX}" "${CMAKE_INSTALL_PREFIX}/share" ) + +# only search for libraries and includes in the ndk toolchain +set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY ) +set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) +set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) + + +# macro to find packages on the host OS +macro( find_host_package ) + set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER ) + set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER ) + set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER ) + if( CMAKE_HOST_WIN32 ) + SET( WIN32 1 ) + SET( UNIX ) + elseif( CMAKE_HOST_APPLE ) + SET( APPLE 1 ) + SET( UNIX ) + endif() + find_package( ${ARGN} ) + SET( WIN32 ) + SET( APPLE ) + SET( UNIX 1 ) + set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY ) + set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) + set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) +endmacro() + + +# macro to find programs on the host OS +macro( find_host_program ) + set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER ) + set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER ) + set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER ) + if( CMAKE_HOST_WIN32 ) + SET( WIN32 1 ) + SET( UNIX ) + elseif( CMAKE_HOST_APPLE ) + SET( APPLE 1 ) + SET( UNIX ) + endif() + find_program( ${ARGN} ) + SET( WIN32 ) + SET( APPLE ) + SET( UNIX 1 ) + set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY ) + set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) + set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) +endmacro() + + +# export toolchain settings for the try_compile() command +if( NOT _CMAKE_IN_TRY_COMPILE ) + set( __toolchain_config "") + foreach( __var NDK_CCACHE LIBRARY_OUTPUT_PATH_ROOT ANDROID_FORBID_SYGWIN + ANDROID_NDK_HOST_X64 + ANDROID_NDK + ANDROID_NDK_LAYOUT + ANDROID_STANDALONE_TOOLCHAIN + ANDROID_TOOLCHAIN_NAME + ANDROID_ABI + ANDROID_NATIVE_API_LEVEL + ANDROID_STL + ANDROID_STL_FORCE_FEATURES + ANDROID_FORCE_ARM_BUILD + ANDROID_NO_UNDEFINED + ANDROID_SO_UNDEFINED + ANDROID_FUNCTION_LEVEL_LINKING + ANDROID_GOLD_LINKER + ANDROID_NOEXECSTACK + ANDROID_RELRO + ANDROID_LIBM_PATH + ANDROID_EXPLICIT_CRT_LINK + ANDROID_APP_PIE + ) + if( DEFINED ${__var} ) + if( ${__var} MATCHES " ") + set( __toolchain_config "${__toolchain_config}set( ${__var} \"${${__var}}\" CACHE INTERNAL \"\" )\n" ) + else() + set( __toolchain_config "${__toolchain_config}set( ${__var} ${${__var}} CACHE INTERNAL \"\" )\n" ) + endif() + endif() + endforeach() + file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/android.toolchain.config.cmake" "${__toolchain_config}" ) + unset( __toolchain_config ) +endif() + + +# force cmake to produce / instead of \ in build commands for Ninja generator +if( CMAKE_GENERATOR MATCHES "Ninja" AND CMAKE_HOST_WIN32 ) + # it is a bad hack after all + # CMake generates Ninja makefiles with UNIX paths only if it thinks that we are going to build with MinGW + set( CMAKE_COMPILER_IS_MINGW TRUE ) # tell CMake that we are MinGW + set( CMAKE_CROSSCOMPILING TRUE ) # stop recursion + enable_language( C ) + enable_language( CXX ) + # unset( CMAKE_COMPILER_IS_MINGW ) # can't unset because CMake does not convert back-slashes in response files without it + unset( MINGW ) +endif() + + +# Variables controlling behavior or set by cmake toolchain: +# ANDROID_ABI : "armeabi-v7a" (default), "armeabi", "armeabi-v7a with NEON", "armeabi-v7a with VFPV3", "armeabi-v6 with VFP", "x86", "mips", "arm64-v8a", "x86_64", "mips64" +# ANDROID_NATIVE_API_LEVEL : 3,4,5,8,9,14,15,16,17,18,19,21 (depends on NDK version) +# ANDROID_STL : gnustl_static/gnustl_shared/stlport_static/stlport_shared/gabi++_static/gabi++_shared/system_re/system/none +# ANDROID_FORBID_SYGWIN : ON/OFF +# ANDROID_NO_UNDEFINED : ON/OFF +# ANDROID_SO_UNDEFINED : OFF/ON (default depends on NDK version) +# ANDROID_FUNCTION_LEVEL_LINKING : ON/OFF +# ANDROID_GOLD_LINKER : ON/OFF +# ANDROID_NOEXECSTACK : ON/OFF +# ANDROID_RELRO : ON/OFF +# ANDROID_FORCE_ARM_BUILD : ON/OFF +# ANDROID_STL_FORCE_FEATURES : ON/OFF +# ANDROID_LIBM_PATH : path to libm.so (set to something like $(TOP)/out/target/product//obj/lib/libm.so) to workaround unresolved `sincos` +# Can be set only at the first run: +# ANDROID_NDK : path to your NDK install +# NDK_CCACHE : path to your ccache executable +# ANDROID_TOOLCHAIN_NAME : the NDK name of compiler toolchain +# ANDROID_NDK_HOST_X64 : try to use x86_64 toolchain (default for x64 host systems) +# ANDROID_NDK_LAYOUT : the inner NDK structure (RELEASE, LINARO, ANDROID) +# LIBRARY_OUTPUT_PATH_ROOT : +# ANDROID_STANDALONE_TOOLCHAIN +# +# Primary read-only variables: +# ANDROID : always TRUE +# ARMEABI : TRUE for arm v6 and older devices +# ARMEABI_V6 : TRUE for arm v6 +# ARMEABI_V7A : TRUE for arm v7a +# ARM64_V8A : TRUE for arm64-v8a +# NEON : TRUE if NEON unit is enabled +# VFPV3 : TRUE if VFP version 3 is enabled +# X86 : TRUE if configured for x86 +# X86_64 : TRUE if configured for x86_64 +# MIPS : TRUE if configured for mips +# MIPS64 : TRUE if configured for mips64 +# BUILD_WITH_ANDROID_NDK : TRUE if NDK is used +# BUILD_WITH_STANDALONE_TOOLCHAIN : TRUE if standalone toolchain is used +# ANDROID_NDK_HOST_SYSTEM_NAME : "windows", "linux-x86" or "darwin-x86" depending on host platform +# ANDROID_NDK_ABI_NAME : "armeabi", "armeabi-v7a", "x86", "mips", "arm64-v8a", "x86_64", "mips64" depending on ANDROID_ABI +# ANDROID_NDK_RELEASE : from r5 to r10d; set only for NDK +# ANDROID_NDK_RELEASE_NUM : numeric ANDROID_NDK_RELEASE version (1000*major+minor) +# ANDROID_ARCH_NAME : "arm", "x86", "mips", "arm64", "x86_64", "mips64" depending on ANDROID_ABI +# ANDROID_SYSROOT : path to the compiler sysroot +# TOOL_OS_SUFFIX : "" or ".exe" depending on host platform +# ANDROID_COMPILER_IS_CLANG : TRUE if clang compiler is used +# +# Secondary (less stable) read-only variables: +# ANDROID_COMPILER_VERSION : GCC version used (not Clang version) +# ANDROID_CLANG_VERSION : version of clang compiler if clang is used +# ANDROID_CXX_FLAGS : C/C++ compiler flags required by Android platform +# ANDROID_SUPPORTED_ABIS : list of currently allowed values for ANDROID_ABI +# ANDROID_TOOLCHAIN_MACHINE_NAME : "arm-linux-androideabi", "arm-eabi" or "i686-android-linux" +# ANDROID_TOOLCHAIN_ROOT : path to the top level of toolchain (standalone or placed inside NDK) +# ANDROID_CLANG_TOOLCHAIN_ROOT : path to clang tools +# ANDROID_SUPPORTED_NATIVE_API_LEVELS : list of native API levels found inside NDK +# ANDROID_STL_INCLUDE_DIRS : stl include paths +# ANDROID_RTTI : if rtti is enabled by the runtime +# ANDROID_EXCEPTIONS : if exceptions are enabled by the runtime +# ANDROID_GCC_TOOLCHAIN_NAME : read-only, differs from ANDROID_TOOLCHAIN_NAME only if clang is used +# +# Defaults: +# ANDROID_DEFAULT_NDK_API_LEVEL +# ANDROID_DEFAULT_NDK_API_LEVEL_${ARCH} +# ANDROID_NDK_SEARCH_PATHS +# ANDROID_SUPPORTED_ABIS_${ARCH} +# ANDROID_SUPPORTED_NDK_VERSIONS diff --git a/contrib/android-cmake/ndk_links.md b/contrib/android-cmake/ndk_links.md new file mode 100644 index 000000000..6d93d61d2 --- /dev/null +++ b/contrib/android-cmake/ndk_links.md @@ -0,0 +1,211 @@ + +============== r1 ============== (dead links) + +* http://dl.google.com/android/ndk/android-ndk-1.5_r1-windows.zip +* http://dl.google.com/android/ndk/android-ndk-1.5_r1-darwin-x86.zip +* http://dl.google.com/android/ndk/android-ndk-1.5_r1-linux-x86.zip + +============== r2 ============== + +* http://dl.google.com/android/ndk/android-ndk-1.6_r1-windows.zip +* http://dl.google.com/android/ndk/android-ndk-1.6_r1-darwin-x86.zip +* http://dl.google.com/android/ndk/android-ndk-1.6_r1-linux-x86.zip + +============== r3 ============== + +* http://dl.google.com/android/ndk/android-ndk-r3-windows.zip +* http://dl.google.com/android/ndk/android-ndk-r3-darwin-x86.zip +* http://dl.google.com/android/ndk/android-ndk-r3-linux-x86.zip + +============== r4 ============== + +* http://dl.google.com/android/ndk/android-ndk-r4-windows.zip +* http://dl.google.com/android/ndk/android-ndk-r4-darwin-x86.zip +* http://dl.google.com/android/ndk/android-ndk-r4-linux-x86.zip + +============== r4b ============== + +* http://dl.google.com/android/ndk/android-ndk-r4b-windows.zip +* http://dl.google.com/android/ndk/android-ndk-r4b-darwin-x86.zip +* http://dl.google.com/android/ndk/android-ndk-r4b-linux-x86.zip + +============== r5 ============== + +* http://dl.google.com/android/ndk/android-ndk-r5-windows.zip +* http://dl.google.com/android/ndk/android-ndk-r5-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r5-linux-x86.tar.bz2 + +============== r5b ============== + +* http://dl.google.com/android/ndk/android-ndk-r5b-windows.zip +* http://dl.google.com/android/ndk/android-ndk-r5b-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r5b-linux-x86.tar.bz2 + +============== r5c ============== + +* http://dl.google.com/android/ndk/android-ndk-r5c-windows.zip +* http://dl.google.com/android/ndk/android-ndk-r5c-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r5c-linux-x86.tar.bz2 + +============== r6 ============== + +* http://dl.google.com/android/ndk/android-ndk-r6-windows.zip +* http://dl.google.com/android/ndk/android-ndk-r6-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r6-linux-x86.tar.bz2 + +============== r6b ============== + +* http://dl.google.com/android/ndk/android-ndk-r6b-windows.zip +* http://dl.google.com/android/ndk/android-ndk-r6b-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r6b-linux-x86.tar.bz2 + +============== r7 ============== + +* http://dl.google.com/android/ndk/android-ndk-r7-windows.zip +* http://dl.google.com/android/ndk/android-ndk-r7-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r7-linux-x86.tar.bz2 + +============== r7b ============== + +* http://dl.google.com/android/ndk/android-ndk-r7b-windows.zip +* http://dl.google.com/android/ndk/android-ndk-r7b-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r7b-linux-x86.tar.bz2 + +============== r7c ============== + +* http://dl.google.com/android/ndk/android-ndk-r7c-windows.zip +* http://dl.google.com/android/ndk/android-ndk-r7c-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r7c-linux-x86.tar.bz2 + +============== r8 ============== + +* http://dl.google.com/android/ndk/android-ndk-r8-windows.zip +* http://dl.google.com/android/ndk/android-ndk-r8-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r8-linux-x86.tar.bz2 + +============== r8b ============== + +* http://dl.google.com/android/ndk/android-ndk-r8b-windows.zip +* http://dl.google.com/android/ndk/android-ndk-r8b-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r8b-linux-x86.tar.bz2 + +============== r8c ============== + +* http://dl.google.com/android/ndk/android-ndk-r8c-windows.zip +* http://dl.google.com/android/ndk/android-ndk-r8c-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r8c-linux-x86.tar.bz2 + +============== r8d ============== + +* http://dl.google.com/android/ndk/android-ndk-r8d-windows.zip +* http://dl.google.com/android/ndk/android-ndk-r8d-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r8d-linux-x86.tar.bz2 + +============== r8e ============== + +* http://dl.google.com/android/ndk/android-ndk-r8e-windows-x86.zip +* http://dl.google.com/android/ndk/android-ndk-r8e-windows-x86_64.zip +* http://dl.google.com/android/ndk/android-ndk-r8e-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r8e-darwin-x86_64.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r8e-linux-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r8e-linux-x86_64.tar.bz2 + +============== r9 ============== + +* http://dl.google.com/android/ndk/android-ndk-r9-windows-x86.zip +* http://dl.google.com/android/ndk/android-ndk-r9-windows-x86-legacy-toolchains.zip +* http://dl.google.com/android/ndk/android-ndk-r9-windows-x86_64.zip +* http://dl.google.com/android/ndk/android-ndk-r9-windows-x86_64-legacy-toolchains.zip +* http://dl.google.com/android/ndk/android-ndk-r9-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9-darwin-x86-legacy-toolchains.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9-darwin-x86_64.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9-darwin-x86_64-legacy-toolchains.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9-linux-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9-linux-x86-legacy-toolchains.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9-linux-x86_64.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9-linux-x86_64-legacy-toolchains.tar.bz2 + +============== r9b ============== + +* http://dl.google.com/android/ndk/android-ndk-r9b-windows-x86.zip +* http://dl.google.com/android/ndk/android-ndk-r9b-windows-x86-legacy-toolchains.zip +* http://dl.google.com/android/ndk/android-ndk-r9b-windows-x86_64.zip +* http://dl.google.com/android/ndk/android-ndk-r9b-windows-x86_64-legacy-toolchains.zip +* http://dl.google.com/android/ndk/android-ndk-r9b-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9b-darwin-x86-legacy-toolchains.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9b-darwin-x86_64.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9b-darwin-x86_64-legacy-toolchains.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9b-linux-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9b-linux-x86-legacy-toolchains.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9b-linux-x86_64.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9b-linux-x86_64-legacy-toolchains.tar.bz2 + +============== r9c ============== + +* http://dl.google.com/android/ndk/android-ndk-r9c-windows-x86.zip +* http://dl.google.com/android/ndk/android-ndk-r9c-windows-x86_64.zip +* http://dl.google.com/android/ndk/android-ndk-r9c-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9c-darwin-x86_64.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9c-linux-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9c-linux-x86_64.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9c-cxx-stl-libs-with-debugging-info.zip + +============== r9d ============== + +* http://dl.google.com/android/ndk/android-ndk-r9d-windows-x86.zip +* http://dl.google.com/android/ndk/android-ndk-r9d-windows-x86_64.zip +* http://dl.google.com/android/ndk/android-ndk-r9d-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9d-darwin-x86_64.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9d-linux-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9d-linux-x86_64.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r9d-cxx-stl-libs-with-debug-info.zip + +============== r10 ============== + +* http://dl.google.com/android/ndk/android-ndk32-r10-windows-x86.zip +* http://dl.google.com/android/ndk/android-ndk32-r10-windows-x86_64.zip +* http://dl.google.com/android/ndk/android-ndk32-r10-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk32-r10-darwin-x86_64.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk32-r10-linux-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk32-r10-linux-x86_64.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk64-r10-windows-x86.zip +* http://dl.google.com/android/ndk/android-ndk64-r10-windows-x86_64.zip +* http://dl.google.com/android/ndk/android-ndk64-r10-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk64-r10-darwin-x86_64.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk64-r10-linux-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk64-r10-linux-x86_64.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r10-cxx-stl-libs-with-debug-info.zip + +============== r10b ============== + +* http://dl.google.com/android/ndk/android-ndk32-r10b-windows-x86.zip +* http://dl.google.com/android/ndk/android-ndk32-r10b-windows-x86_64.zip +* http://dl.google.com/android/ndk/android-ndk32-r10b-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk32-r10b-darwin-x86_64.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk32-r10b-linux-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk32-r10b-linux-x86_64.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk64-r10b-windows-x86.zip +* http://dl.google.com/android/ndk/android-ndk64-r10b-windows-x86_64.zip +* http://dl.google.com/android/ndk/android-ndk64-r10b-darwin-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk64-r10b-darwin-x86_64.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk64-r10b-linux-x86.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk64-r10b-linux-x86_64.tar.bz2 +* http://dl.google.com/android/ndk/android-ndk-r10b-cxx-stl-libs-with-debug-info.zip + +============== r10c ============== + +* http://dl.google.com/android/ndk/android-ndk-r10c-windows-x86.exe +* http://dl.google.com/android/ndk/android-ndk-r10c-windows-x86_64.exe +* http://dl.google.com/android/ndk/android-ndk-r10c-darwin-x86.bin +* http://dl.google.com/android/ndk/android-ndk-r10c-darwin-x86_64.bin +* http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86.bin +* http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin + +============== r10d ============== + +* http://dl.google.com/android/ndk/android-ndk-r10d-windows-x86.exe +* http://dl.google.com/android/ndk/android-ndk-r10d-windows-x86_64.exe +* http://dl.google.com/android/ndk/android-ndk-r10d-darwin-x86.bin +* http://dl.google.com/android/ndk/android-ndk-r10d-darwin-x86_64.bin +* http://dl.google.com/android/ndk/android-ndk-r10d-linux-x86.bin +* http://dl.google.com/android/ndk/android-ndk-r10d-linux-x86_64.bin diff --git a/contrib/zlib/zconf.h.included b/contrib/zlib/zconf.h.included index 5e1d68a00..352f552b8 100644 --- a/contrib/zlib/zconf.h.included +++ b/contrib/zlib/zconf.h.included @@ -7,6 +7,8 @@ #ifndef ZCONF_H #define ZCONF_H +/* #undef Z_PREFIX */ +/* #undef Z_HAVE_UNISTD_H */ /* * If you *really* need a unique prefix for all types and library functions, diff --git a/scripts/android_crosscompile/make_android.bat b/scripts/android_crosscompile/make_android.bat new file mode 100644 index 000000000..a50695e16 --- /dev/null +++ b/scripts/android_crosscompile/make_android.bat @@ -0,0 +1,28 @@ +@echo off + +set ASSIMP_PATH=D:\projects\asset-importer-lib\assimp +set CMAKE_PATH="C:\Program Files\CMake\bin\cmake.exe" +set ANDROID_NDK_PATH=C:\Users\kimkulling\AppData\Local\Android\Sdk\ndk-bundle +set ANDROID_CMAKE_PATH=contrib\android-cmake + +pushd %ASSIMP_PATH% + +rmdir /s /q build +mkdir build +cd build + +%CMAKE_PATH% .. ^ + -G"MinGW Makefiles" ^ + -DCMAKE_BUILD_TYPE=Release ^ + -DCMAKE_TOOLCHAIN_FILE=%ANDROID_CMAKE_PATH%\android.toolchain.cmake ^ + -DCMAKE_MAKE_PROGRAM=%ANDROID_NDK_PATH%\prebuilt\windows-x86_64\bin\make.exe ^ + -DANDROID_NDK=%ANDROID_NDK_PATH% ^ + -DANDROID_NATIVE_API_LEVEL=android-9 ^ + -DASSIMP_ANDROID_JNIIOSYSTEM=ON ^ + -DANDROID_ABI=arm64-v8a ^ + -DASSIMP_BUILD_ZLIB=ON ^ + -DASSIMP_BUILD_TESTS=OFF + +%CMAKE_PATH% --build . + +popd \ No newline at end of file From d9965f6220b500b35a2186bebd32b4b49b3e784a Mon Sep 17 00:00:00 2001 From: Giuseppe Barbieri Date: Thu, 16 Nov 2017 14:42:20 +0100 Subject: [PATCH 343/490] Update Importer.cpp --- code/Importer.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/code/Importer.cpp b/code/Importer.cpp index 379daab02..c6c6f0edd 100644 --- a/code/Importer.cpp +++ b/code/Importer.cpp @@ -274,10 +274,6 @@ aiReturn Importer::UnregisterLoader(BaseImporter* pImp) if (it != pimpl->mImporter.end()) { pimpl->mImporter.erase(it); - - std::set st; - pImp->GetExtensionList(st); - DefaultLogger::get()->info("Unregistering custom importer: "); return AI_SUCCESS; } From b428c66f3931d6c7ea2be0437b735b2506722a5e Mon Sep 17 00:00:00 2001 From: Giuseppe Barbieri Date: Fri, 17 Nov 2017 23:10:05 +0100 Subject: [PATCH 344/490] Update ValidateDataStructure.h --- code/ValidateDataStructure.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ValidateDataStructure.h b/code/ValidateDataStructure.h index 6daf9b87d..c53207483 100644 --- a/code/ValidateDataStructure.h +++ b/code/ValidateDataStructure.h @@ -165,7 +165,7 @@ private: inline void DoValidation(T** array, unsigned int size, const char* firstName, const char* secondName); - // extended version: checks whethr T::mName occurs twice + // extended version: checks whether T::mName occurs twice template inline void DoValidationEx(T** array, unsigned int size, const char* firstName, const char* secondName); From fc360b9cc8c69f2817a9f65eb5f6acc8600b7175 Mon Sep 17 00:00:00 2001 From: Giuseppe Barbieri Date: Sat, 18 Nov 2017 00:05:17 +0100 Subject: [PATCH 345/490] Update ValidateDataStructure.h --- code/ValidateDataStructure.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ValidateDataStructure.h b/code/ValidateDataStructure.h index 6daf9b87d..188950e0e 100644 --- a/code/ValidateDataStructure.h +++ b/code/ValidateDataStructure.h @@ -91,7 +91,7 @@ protected: // ------------------------------------------------------------------- /** Report a validation warning. This won't throw an exception, - * control will return to the callera. + * control will return to the caller. * @param msg Format string for sprintf().*/ void ReportWarning(const char* msg,...); From 703d046fd9baf6f70ce78c96a1567e9675a884eb Mon Sep 17 00:00:00 2001 From: Giuseppe Barbieri Date: Sat, 18 Nov 2017 00:34:00 +0100 Subject: [PATCH 346/490] Update ValidateDataStructure.cpp --- code/ValidateDataStructure.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/ValidateDataStructure.cpp b/code/ValidateDataStructure.cpp index f1035e441..c036eac18 100644 --- a/code/ValidateDataStructure.cpp +++ b/code/ValidateDataStructure.cpp @@ -334,28 +334,28 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh) case 1: if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POINT)) { - ReportError("aiMesh::mFaces[%i] is a POINT but aiMesh::mPrimtiveTypes " + ReportError("aiMesh::mFaces[%i] is a POINT but aiMesh::mPrimitiveTypes " "does not report the POINT flag",i); } break; case 2: if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_LINE)) { - ReportError("aiMesh::mFaces[%i] is a LINE but aiMesh::mPrimtiveTypes " + ReportError("aiMesh::mFaces[%i] is a LINE but aiMesh::mPrimitiveTypes " "does not report the LINE flag",i); } break; case 3: if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE)) { - ReportError("aiMesh::mFaces[%i] is a TRIANGLE but aiMesh::mPrimtiveTypes " + ReportError("aiMesh::mFaces[%i] is a TRIANGLE but aiMesh::mPrimitiveTypes " "does not report the TRIANGLE flag",i); } break; default: if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) { - this->ReportError("aiMesh::mFaces[%i] is a POLYGON but aiMesh::mPrimtiveTypes " + this->ReportError("aiMesh::mFaces[%i] is a POLYGON but aiMesh::mPrimitiveTypes " "does not report the POLYGON flag",i); } break; From 40bb3f3d0f7998588273565d6eee1853f4593f9a Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Fri, 17 Nov 2017 13:56:53 +0200 Subject: [PATCH 347/490] Unit test: Fix signed/unsigned comparison warnings --- test/unit/utObjImportExport.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/unit/utObjImportExport.cpp b/test/unit/utObjImportExport.cpp index 6361813d0..d9df779ac 100644 --- a/test/unit/utObjImportExport.cpp +++ b/test/unit/utObjImportExport.cpp @@ -294,12 +294,12 @@ TEST_F(utObjImportExport, relative_indices_Test) { const aiScene *scene = myimporter.ReadFileFromMemory(ObjModel.c_str(), ObjModel.size(), aiProcess_ValidateDataStructure); EXPECT_NE(nullptr, scene); - EXPECT_EQ(scene->mNumMeshes, 1); + EXPECT_EQ(scene->mNumMeshes, 1U); const aiMesh *mesh = scene->mMeshes[0]; - EXPECT_EQ(mesh->mNumVertices, 4); - EXPECT_EQ(mesh->mNumFaces, 1); + EXPECT_EQ(mesh->mNumVertices, 4U); + EXPECT_EQ(mesh->mNumFaces, 1U); const aiFace face = mesh->mFaces[0]; - EXPECT_EQ(face.mNumIndices, 4); + EXPECT_EQ(face.mNumIndices, 4U); for (unsigned int i = 0; i < face.mNumIndices; ++i) { EXPECT_EQ(face.mIndices[i], i); @@ -318,12 +318,12 @@ TEST_F(utObjImportExport, homogeneous_coordinates_Test) { const aiScene *scene = myimporter.ReadFileFromMemory(ObjModel.c_str(), ObjModel.size(), aiProcess_ValidateDataStructure); EXPECT_NE(nullptr, scene); - EXPECT_EQ(scene->mNumMeshes, 1); + EXPECT_EQ(scene->mNumMeshes, 1U); const aiMesh *mesh = scene->mMeshes[0]; - EXPECT_EQ(mesh->mNumVertices, 3); - EXPECT_EQ(mesh->mNumFaces, 1); + EXPECT_EQ(mesh->mNumVertices, 3U); + EXPECT_EQ(mesh->mNumFaces, 1U); const aiFace face = mesh->mFaces[0]; - EXPECT_EQ(face.mNumIndices, 3); + EXPECT_EQ(face.mNumIndices, 3U); const aiVector3D vertice = mesh->mVertices[0]; EXPECT_EQ(vertice.x, -1.0f); EXPECT_EQ(vertice.y, 0.0f); From c1515db56f951cec82dbfa35265db35d7d8bfd71 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 18 Nov 2017 15:50:56 +0200 Subject: [PATCH 348/490] Blender: Fix strict-aliasing warnings --- code/BlenderScene.cpp | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/code/BlenderScene.cpp b/code/BlenderScene.cpp index b9068329b..0e1dec7f3 100644 --- a/code/BlenderScene.cpp +++ b/code/BlenderScene.cpp @@ -59,7 +59,9 @@ template <> void Structure :: Convert ( { ReadField(dest.id,"id",db); - ReadField((int&)dest.type,"type",db); + int temp = 0; + ReadField(temp,"type",db); + dest.type = static_cast(temp); ReadFieldArray2(dest.obmat,"obmat",db); ReadFieldArray2(dest.parentinv,"parentinv",db); ReadFieldArray(dest.parsubstr,"parsubstr",db); @@ -100,14 +102,21 @@ template <> void Structure :: Convert ( ) const { - ReadField((short&)dest.mapto,"mapto",db); - ReadField((int&)dest.blendtype,"blendtype",db); + int temp_short = 0; + ReadField(temp_short,"mapto",db); + dest.mapto = static_cast(temp_short); + int temp = 0; + ReadField(temp,"blendtype",db); + dest.blendtype = static_cast(temp); ReadFieldPtr(dest.object,"*object",db); ReadFieldPtr(dest.tex,"*tex",db); ReadFieldArray(dest.uvname,"uvname",db); - ReadField((int&)dest.projx,"projx",db); - ReadField((int&)dest.projy,"projy",db); - ReadField((int&)dest.projz,"projz",db); + ReadField(temp,"projx",db); + dest.projx = static_cast(temp); + ReadField(temp,"projy",db); + dest.projy = static_cast(temp); + ReadField(temp,"projz",db); + dest.projx = static_cast(temp); ReadField(dest.mapping,"mapping",db); ReadFieldArray(dest.ofs,"ofs",db); ReadFieldArray(dest.size,"size",db); @@ -190,7 +199,9 @@ template <> void Structure :: Convert ( { ReadField(dest.id,"id",db); - ReadField((int&)dest.type,"type",db); + int temp = 0; + ReadField(temp,"type",db); + dest.type = static_cast(temp); ReadField(dest.flags,"flags",db); ReadField(dest.colormodel,"colormodel",db); ReadField(dest.totex,"totex",db); @@ -204,7 +215,8 @@ template <> void Structure :: Convert ( ReadField(dest.spotblend,"spotblend",db); ReadField(dest.att1,"att1",db); ReadField(dest.att2,"att2",db); - ReadField((int&)dest.falloff_type,"falloff_type",db); + ReadField(temp,"falloff_type",db); + dest.falloff_type = static_cast(temp); ReadField(dest.sun_brightness,"sun_brightness",db); ReadField(dest.area_size,"area_size",db); ReadField(dest.area_sizey,"area_sizey",db); @@ -693,8 +705,12 @@ template <> void Structure :: Convert ( const FileDatabase& db ) const { - ReadField((short&)dest.imaflag,"imaflag",db); - ReadField((int&)dest.type,"type",db); + short temp_short = 0; + ReadField(temp_short,"imaflag",db); + dest.imaflag = static_cast(temp_short); + int temp = 0; + ReadField(temp,"type",db); + dest.type = static_cast(temp); ReadFieldPtr(dest.ima,"*ima",db); db.reader->IncPtr(size); @@ -708,8 +724,11 @@ template <> void Structure :: Convert ( { ReadField(dest.id,"id",db); - ReadField((int&)dest.type,"type",db); - ReadField((int&)dest.flag,"flag",db); + int temp = 0; + ReadField(temp,"type",db); + dest.type = static_cast(temp); + ReadField(temp,"flag",db); + dest.flag = static_cast(temp); ReadField(dest.lens,"lens",db); ReadField(dest.sensor_x,"sensor_x",db); ReadField(dest.clipsta,"clipsta",db); From 1067ae4bfa21bd27131f5a332b4f0c615477a97c Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 18 Nov 2017 15:54:40 +0200 Subject: [PATCH 349/490] FIReader: Fix strict-aliasing warnings --- code/FIReader.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/code/FIReader.cpp b/code/FIReader.cpp index fdf6b8a83..f3cf3cdb4 100755 --- a/code/FIReader.cpp +++ b/code/FIReader.cpp @@ -485,7 +485,9 @@ struct FIFloatDecoder: public FIDecoder { value.reserve(numFloats); for (size_t i = 0; i < numFloats; ++i) { int v = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - value.push_back(*(float*)&v); + float f; + memcpy(&f, &v, 4); + value.push_back(f); data += 4; } return FIFloatValue::create(std::move(value)); @@ -503,7 +505,9 @@ struct FIDoubleDecoder: public FIDecoder { for (size_t i = 0; i < numDoubles; ++i) { long long b0 = data[0], b1 = data[1], b2 = data[2], b3 = data[3], b4 = data[4], b5 = data[5], b6 = data[6], b7 = data[7]; long long v = (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7; - value.push_back(*(double*)&v); + double f; + memcpy(&f, &v, 8); + value.push_back(f); data += 8; } return FIDoubleValue::create(std::move(value)); From 53119e74f81e06429d82e9ee2882322267af4a59 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 18 Nov 2017 15:59:30 +0200 Subject: [PATCH 350/490] Open3DGC: Fix strict-aliasing warnings --- contrib/Open3DGC/o3dgcBinaryStream.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/contrib/Open3DGC/o3dgcBinaryStream.h b/contrib/Open3DGC/o3dgcBinaryStream.h index 19e3df973..b7b7678b7 100644 --- a/contrib/Open3DGC/o3dgcBinaryStream.h +++ b/contrib/Open3DGC/o3dgcBinaryStream.h @@ -231,7 +231,8 @@ namespace o3dgc float ReadFloat32Bin(unsigned long & position) const { unsigned long value = ReadUInt32Bin(position); - float fvalue = *((float *)(&value)); + float fvalue; + memcpy(&fvalue, &value, 4); return fvalue; } unsigned long ReadUInt32Bin(unsigned long & position) const @@ -261,7 +262,8 @@ namespace o3dgc void WriteFloat32ASCII(float value) { - unsigned long uiValue = *((unsigned long *)(&value)); + unsigned long uiValue; + memcpy(&uiValue, &value, 4); WriteUInt32ASCII(uiValue); } void WriteUInt32ASCII(unsigned long position, unsigned long value) @@ -314,7 +316,8 @@ namespace o3dgc float ReadFloat32ASCII(unsigned long & position) const { unsigned long value = ReadUInt32ASCII(position); - float fvalue = *((float *)(&value)); + float fvalue; + memcpy(&fvalue, &value, 4); return fvalue; } unsigned long ReadUInt32ASCII(unsigned long & position) const From 10f4b6f95c318e92b40e1e79ff09a2eca511f136 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 18 Nov 2017 16:02:53 +0200 Subject: [PATCH 351/490] assimp_cmd: Fix strict-aliasing warnings --- tools/assimp_cmd/WriteDumb.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tools/assimp_cmd/WriteDumb.cpp b/tools/assimp_cmd/WriteDumb.cpp index 69d4efcc5..e1b104d40 100644 --- a/tools/assimp_cmd/WriteDumb.cpp +++ b/tools/assimp_cmd/WriteDumb.cpp @@ -177,6 +177,17 @@ inline uint32_t Write(const aiVector3D& v) return t; } +// ----------------------------------------------------------------------------------- +// Serialize a color value +template <> +inline uint32_t Write(const aiColor3D& v) +{ + uint32_t t = Write(v.r); + t += Write(v.g); + t += Write(v.b); + return t; +} + // ----------------------------------------------------------------------------------- // Serialize a color value template <> @@ -566,9 +577,9 @@ uint32_t WriteBinaryLight(const aiLight* l) len += Write(l->mAttenuationQuadratic); } - len += Write((const aiVector3D&)l->mColorDiffuse); - len += Write((const aiVector3D&)l->mColorSpecular); - len += Write((const aiVector3D&)l->mColorAmbient); + len += Write(l->mColorDiffuse); + len += Write(l->mColorSpecular); + len += Write(l->mColorAmbient); if (l->mType == aiLightSource_SPOT) { len += Write(l->mAngleInnerCone); From bcffa28a336b4ce9350b2159649fe5258aedbca7 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 18 Nov 2017 16:39:43 +0200 Subject: [PATCH 352/490] MDLImporter: Don't take address of packed struct member --- code/MDLMaterialLoader.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/MDLMaterialLoader.cpp b/code/MDLMaterialLoader.cpp index 91dcb49cc..9086925aa 100644 --- a/code/MDLMaterialLoader.cpp +++ b/code/MDLMaterialLoader.cpp @@ -665,7 +665,9 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7( if (0.0f != pcMatIn->Power) { iShadingMode = (int)aiShadingMode_Phong; - pcMatOut->AddProperty(&pcMatIn->Power,1,AI_MATKEY_SHININESS); + // pcMatIn is packed, we can't form pointers to its members + float power = pcMatIn->Power; + pcMatOut->AddProperty(&power,1,AI_MATKEY_SHININESS); } pcMatOut->AddProperty(&iShadingMode,1,AI_MATKEY_SHADING_MODEL); } From 2c8cc1f73249246d183d3e4d1aa84b4a3325f6ef Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 18 Nov 2017 16:49:03 +0200 Subject: [PATCH 353/490] BlenderDNA: Silence warning about uninitialized member --- code/BlenderDNA.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/BlenderDNA.h b/code/BlenderDNA.h index 4c7d0fc26..bac8d78bc 100644 --- a/code/BlenderDNA.h +++ b/code/BlenderDNA.h @@ -92,6 +92,12 @@ struct Error : DeadlyImportError { * descendents. It serves as base class for all data structure fields. */ // ------------------------------------------------------------------------------- struct ElemBase { + ElemBase() + : dna_type(nullptr) + { + // empty + } + virtual ~ElemBase() { // empty } From 8ae723165439087a6f45776b397fff03bcc550da Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 18 Nov 2017 16:50:45 +0200 Subject: [PATCH 354/490] Blender: Silence warning about uninitialized member --- code/BlenderScene.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/code/BlenderScene.h b/code/BlenderScene.h index 36094eabd..86ab1f30c 100644 --- a/code/BlenderScene.h +++ b/code/BlenderScene.h @@ -225,6 +225,14 @@ struct TFace : ElemBase { // ------------------------------------------------------------------------------- struct MTFace : ElemBase { + MTFace() + : flag(0) + , mode(0) + , tile(0) + , unwrap(0) + { + } + float uv[4][2] FAIL; char flag; short mode; From a8e65a1e8aff4a254453f26719df95f8cacdaadf Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 18 Nov 2017 18:32:16 +0100 Subject: [PATCH 355/490] Fix android build. --- CMakeLists.txt | 2 +- code/FIReader.cpp | 24 +++++++++++++----------- code/FIReader.hpp | 10 ++++++++-- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 79db04762..73a30e9d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,7 +186,7 @@ IF( UNIX ) IF( ${OPERATING_SYSTEM} MATCHES "Android") ELSE() IF ( CMAKE_SIZEOF_VOID_P EQUAL 4) # only necessary for 32-bit linux - ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 ) + #ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 ) ENDIF() ENDIF() diff --git a/code/FIReader.cpp b/code/FIReader.cpp index fdf6b8a83..e167db026 100755 --- a/code/FIReader.cpp +++ b/code/FIReader.cpp @@ -45,13 +45,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER +#include "FIReader.hpp" +#include "StringUtils.h" + // Workaround for issue #1361 // https://github.com/assimp/assimp/issues/1361 #ifdef __ANDROID__ -#define _GLIBCXX_USE_C99 1 +# define _GLIBCXX_USE_C99 1 #endif -#include "FIReader.hpp" #include "Exceptional.h" #include #include @@ -685,7 +687,7 @@ public: if (intValue) { return intValue->value.size() == 1 ? intValue->value.front() : 0; } - return stoi(attr->value->toString()); + return atoi(attr->value->toString().c_str()); } virtual int getAttributeValueAsInt(int idx) const /*override*/ { @@ -696,7 +698,7 @@ public: if (intValue) { return intValue->value.size() == 1 ? intValue->value.front() : 0; } - return stoi(attributes[idx].value->toString()); + return atoi(attributes[idx].value->toString().c_str()); } virtual float getAttributeValueAsFloat(const char* name) const /*override*/ { @@ -708,7 +710,7 @@ public: if (floatValue) { return floatValue->value.size() == 1 ? floatValue->value.front() : 0; } - return stof(attr->value->toString()); + return atof(attr->value->toString().c_str()); } virtual float getAttributeValueAsFloat(int idx) const /*override*/ { @@ -719,7 +721,7 @@ public: if (floatValue) { return floatValue->value.size() == 1 ? floatValue->value.front() : 0; } - return stof(attributes[idx].value->toString()); + return atof(attributes[idx].value->toString().c_str()); } virtual const char* getNodeName() const /*override*/ { @@ -984,13 +986,13 @@ private: if (index < 32) { FIDecoder *decoder = defaultDecoder[index]; if (!decoder) { - throw DeadlyImportError("Invalid encoding algorithm index " + std::to_string(index)); + throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index)); } return decoder->decode(dataP, len); } else { if (index - 32 >= vocabulary.encodingAlgorithmTable.size()) { - throw DeadlyImportError("Invalid encoding algorithm index " + std::to_string(index)); + throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index)); } std::string uri = vocabulary.encodingAlgorithmTable[index - 32]; auto it = decoderMap.find(uri); @@ -1014,12 +1016,12 @@ private: alphabet = "0123456789-:TZ "; break; default: - throw DeadlyImportError("Invalid restricted alphabet index " + std::to_string(index)); + throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index)); } } else { if (index - 16 >= vocabulary.restrictedAlphabetTable.size()) { - throw DeadlyImportError("Invalid restricted alphabet index " + std::to_string(index)); + throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index)); } alphabet = vocabulary.restrictedAlphabetTable[index - 16]; } @@ -1027,7 +1029,7 @@ private: utf8::utf8to32(alphabet.begin(), alphabet.end(), back_inserter(alphabetUTF32)); std::string::size_type alphabetLength = alphabetUTF32.size(); if (alphabetLength < 2) { - throw DeadlyImportError("Invalid restricted alphabet length " + std::to_string(alphabetLength)); + throw DeadlyImportError("Invalid restricted alphabet length " + to_string(alphabetLength)); } std::string::size_type bitsPerCharacter = 1; while ((1ull << bitsPerCharacter) <= alphabetLength) { diff --git a/code/FIReader.hpp b/code/FIReader.hpp index 4e9f712a9..5f4e5bb48 100644 --- a/code/FIReader.hpp +++ b/code/FIReader.hpp @@ -46,12 +46,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef INCLUDED_AI_FI_READER_H #define INCLUDED_AI_FI_READER_H +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +//#include #include +#include #include #include -#include #include -#include +//#include +//#include #include namespace Assimp { @@ -176,4 +180,6 @@ FIReader::~FIReader() { }// namespace Assimp +#endif // #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + #endif // INCLUDED_AI_FI_READER_H From 9707fde709e28e7764dfa22de51ef6755ad46da6 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 19 Nov 2017 17:41:31 +0100 Subject: [PATCH 356/490] check for nullptr dereferencing before copying scene data --- code/ColladaExporter.cpp | 2 +- code/SceneCombiner.cpp | 152 ++++++++++++++++++--------------- include/assimp/SceneCombiner.h | 12 ++- test/unit/utSceneCombiner.cpp | 5 ++ 4 files changed, 97 insertions(+), 74 deletions(-) diff --git a/code/ColladaExporter.cpp b/code/ColladaExporter.cpp index ffef6f067..03a955b8f 100644 --- a/code/ColladaExporter.cpp +++ b/code/ColladaExporter.cpp @@ -1280,7 +1280,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) std::vector frames; for( size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) { - frames.push_back(nodeAnim->mPositionKeys[i].mTime); + frames.push_back(static_cast(nodeAnim->mPositionKeys[i].mTime)); } WriteFloatArray( node_idstr , FloatType_Time, (const ai_real*) frames.data(), frames.size()); diff --git a/code/SceneCombiner.cpp b/code/SceneCombiner.cpp index 6fb120325..608aa8297 100644 --- a/code/SceneCombiner.cpp +++ b/code/SceneCombiner.cpp @@ -66,8 +66,8 @@ namespace Assimp { // ------------------------------------------------------------------------------------------------ // Add a prefix to a string -inline void PrefixString(aiString& string,const char* prefix, unsigned int len) -{ +inline +void PrefixString(aiString& string,const char* prefix, unsigned int len) { // If the string is already prefixed, we won't prefix it a second time if (string.length >= 1 && string.data[0] == '$') return; @@ -88,8 +88,7 @@ inline void PrefixString(aiString& string,const char* prefix, unsigned int len) // ------------------------------------------------------------------------------------------------ // Add node identifiers to a hashing set -void SceneCombiner::AddNodeHashes(aiNode* node, std::set& hashes) -{ +void SceneCombiner::AddNodeHashes(aiNode* node, std::set& hashes) { // Add node name to hashing set if it is non-empty - empty nodes are allowed // and they can't have any anims assigned so its absolutely safe to duplicate them. if (node->mName.length) { @@ -103,8 +102,7 @@ void SceneCombiner::AddNodeHashes(aiNode* node, std::set& hashes) // ------------------------------------------------------------------------------------------------ // Add a name prefix to all nodes in a hierarchy -void SceneCombiner::AddNodePrefixes(aiNode* node, const char* prefix, unsigned int len) -{ +void SceneCombiner::AddNodePrefixes(aiNode* node, const char* prefix, unsigned int len) { ai_assert(NULL != prefix); PrefixString(node->mName,prefix,len); @@ -115,8 +113,7 @@ void SceneCombiner::AddNodePrefixes(aiNode* node, const char* prefix, unsigned i // ------------------------------------------------------------------------------------------------ // Search for matching names -bool SceneCombiner::FindNameMatch(const aiString& name, std::vector& input, unsigned int cur) -{ +bool SceneCombiner::FindNameMatch(const aiString& name, std::vector& input, unsigned int cur) { const unsigned int hash = SuperFastHash(name.data, static_cast(name.length)); // Check whether we find a positive match in one of the given sets @@ -132,8 +129,7 @@ bool SceneCombiner::FindNameMatch(const aiString& name, std::vector // ------------------------------------------------------------------------------------------------ // Add a name prefix to all nodes in a hierarchy if a hash match is found void SceneCombiner::AddNodePrefixesChecked(aiNode* node, const char* prefix, unsigned int len, - std::vector& input, unsigned int cur) -{ + std::vector& input, unsigned int cur) { ai_assert(NULL != prefix); const unsigned int hash = SuperFastHash(node->mName.data, static_cast(node->mName.length)); @@ -165,9 +161,10 @@ void SceneCombiner::OffsetNodeMeshIndices (aiNode* node, unsigned int offset) // ------------------------------------------------------------------------------------------------ // Merges two scenes. Currently only used by the LWS loader. void SceneCombiner::MergeScenes(aiScene** _dest,std::vector& src, - unsigned int flags) -{ - ai_assert(NULL != _dest); + unsigned int flags) { + if ( nullptr == _dest ) { + return; + } // if _dest points to NULL allocate a new scene. Otherwise clear the old and reuse it if (src.empty()) @@ -198,8 +195,7 @@ void SceneCombiner::MergeScenes(aiScene** _dest,std::vector& src, } // ------------------------------------------------------------------------------------------------ -void SceneCombiner::AttachToGraph (aiNode* attach, std::vector& srcList) -{ +void SceneCombiner::AttachToGraph (aiNode* attach, std::vector& srcList) { unsigned int cnt; for ( cnt = 0; cnt < attach->mNumChildren; ++cnt ) { AttachToGraph( attach->mChildren[ cnt ], srcList ); @@ -239,19 +235,16 @@ void SceneCombiner::AttachToGraph (aiNode* attach, std::vector& src) -{ +void SceneCombiner::AttachToGraph ( aiScene* master, std::vector& src) { ai_assert(NULL != master); AttachToGraph(master->mRootNode,src); } // ------------------------------------------------------------------------------------------------ -void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master, - std::vector& srcList, - unsigned int flags) -{ - ai_assert(NULL != _dest); +void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master, std::vector& srcList, unsigned int flags) { + if ( nullptr == _dest ) { + return; + } // if _dest points to NULL allocate a new scene. Otherwise clear the old and reuse it if (srcList.empty()) { @@ -708,7 +701,9 @@ void SceneCombiner::BuildUniqueBoneList(std::list& asBones, void SceneCombiner::MergeBones(aiMesh* out,std::vector::const_iterator it, std::vector::const_iterator end) { - ai_assert(NULL != out && !out->mNumBones); + if ( nullptr == out || out->mNumBones == 0 ) { + return; + } // find we need to build an unique list of all bones. // we work with hashes to make the comparisons MUCH faster, @@ -762,7 +757,9 @@ void SceneCombiner::MergeMeshes(aiMesh** _out, unsigned int /*flags*/, std::vector::const_iterator begin, std::vector::const_iterator end) { - ai_assert(NULL != _out); + if ( nullptr == _out ) { + return; + } if (begin == end) { *_out = NULL; // no meshes ... @@ -903,7 +900,9 @@ void SceneCombiner::MergeMaterials(aiMaterial** dest, std::vector::const_iterator begin, std::vector::const_iterator end) { - ai_assert(NULL != dest); + if ( nullptr == dest ) { + return; + } if (begin == end) { *dest = NULL; // no materials ... @@ -953,10 +952,9 @@ void SceneCombiner::MergeMaterials(aiMaterial** dest, // ------------------------------------------------------------------------------------------------ template -inline void CopyPtrArray (Type**& dest, const Type* const * src, ai_uint num) -{ - if (!num) - { +inline +void CopyPtrArray (Type**& dest, const Type* const * src, ai_uint num) { + if (!num) { dest = NULL; return; } @@ -968,9 +966,11 @@ inline void CopyPtrArray (Type**& dest, const Type* const * src, ai_uint num) // ------------------------------------------------------------------------------------------------ template -inline void GetArrayCopy (Type*& dest, ai_uint num ) -{ - if (!dest)return; +inline +void GetArrayCopy(Type*& dest, ai_uint num ) { + if ( !dest ) { + return; + } Type* old = dest; dest = new Type[num]; @@ -978,22 +978,27 @@ inline void GetArrayCopy (Type*& dest, ai_uint num ) } // ------------------------------------------------------------------------------------------------ -void SceneCombiner::CopySceneFlat(aiScene** _dest,const aiScene* src) -{ +void SceneCombiner::CopySceneFlat(aiScene** _dest,const aiScene* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + // reuse the old scene or allocate a new? if (*_dest) { (*_dest)->~aiScene(); new (*_dest) aiScene(); + } else { + *_dest = new aiScene(); } - else *_dest = new aiScene(); ::memcpy(*_dest,src,sizeof(aiScene)); } // ------------------------------------------------------------------------------------------------ -void SceneCombiner::CopyScene(aiScene** _dest,const aiScene* src,bool allocate) -{ - ai_assert(NULL != _dest && NULL != src); +void SceneCombiner::CopyScene(aiScene** _dest,const aiScene* src,bool allocate) { + if ( nullptr == _dest || nullptr == src ) { + return; + } if (allocate) { *_dest = new aiScene(); @@ -1044,9 +1049,10 @@ void SceneCombiner::CopyScene(aiScene** _dest,const aiScene* src,bool allocate) } // ------------------------------------------------------------------------------------------------ -void SceneCombiner::Copy (aiMesh** _dest, const aiMesh* src) -{ - ai_assert(NULL != _dest && NULL != src); +void SceneCombiner::Copy( aiMesh** _dest, const aiMesh* src ) { + if ( nullptr == _dest || nullptr == src ) { + return; + } aiMesh* dest = *_dest = new aiMesh(); @@ -1080,9 +1086,11 @@ void SceneCombiner::Copy (aiMesh** _dest, const aiMesh* src) } // ------------------------------------------------------------------------------------------------ -void SceneCombiner::Copy (aiMaterial** _dest, const aiMaterial* src) -{ - ai_assert(NULL != _dest && NULL != src); +void SceneCombiner::Copy (aiMaterial** _dest, const aiMaterial* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + aiMaterial* dest = (aiMaterial*) ( *_dest = new aiMaterial() ); @@ -1110,9 +1118,10 @@ void SceneCombiner::Copy (aiMaterial** _dest, const aiMaterial* src) } // ------------------------------------------------------------------------------------------------ -void SceneCombiner::Copy (aiTexture** _dest, const aiTexture* src) -{ - ai_assert(NULL != _dest && NULL != src); +void SceneCombiner::Copy(aiTexture** _dest, const aiTexture* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } aiTexture* dest = *_dest = new aiTexture(); @@ -1139,10 +1148,10 @@ void SceneCombiner::Copy (aiTexture** _dest, const aiTexture* src) } // ------------------------------------------------------------------------------------------------ -void SceneCombiner::Copy( aiAnimation** _dest, const aiAnimation* src ) -{ - ai_assert( NULL != _dest ); - ai_assert( NULL != src ); +void SceneCombiner::Copy( aiAnimation** _dest, const aiAnimation* src ) { + if ( nullptr == _dest || nullptr == src ) { + return; + } aiAnimation* dest = *_dest = new aiAnimation(); @@ -1154,9 +1163,10 @@ void SceneCombiner::Copy( aiAnimation** _dest, const aiAnimation* src ) } // ------------------------------------------------------------------------------------------------ -void SceneCombiner::Copy (aiNodeAnim** _dest, const aiNodeAnim* src) -{ - ai_assert(NULL != _dest && NULL != src); +void SceneCombiner::Copy(aiNodeAnim** _dest, const aiNodeAnim* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } aiNodeAnim* dest = *_dest = new aiNodeAnim(); @@ -1170,9 +1180,10 @@ void SceneCombiner::Copy (aiNodeAnim** _dest, const aiNodeAnim* src) } // ------------------------------------------------------------------------------------------------ -void SceneCombiner::Copy (aiCamera** _dest,const aiCamera* src) -{ - ai_assert(NULL != _dest && NULL != src); +void SceneCombiner::Copy( aiCamera** _dest,const aiCamera* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } aiCamera* dest = *_dest = new aiCamera(); @@ -1181,9 +1192,10 @@ void SceneCombiner::Copy (aiCamera** _dest,const aiCamera* src) } // ------------------------------------------------------------------------------------------------ -void SceneCombiner::Copy (aiLight** _dest, const aiLight* src) -{ - ai_assert(NULL != _dest && NULL != src); +void SceneCombiner::Copy(aiLight** _dest, const aiLight* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } aiLight* dest = *_dest = new aiLight(); @@ -1192,9 +1204,10 @@ void SceneCombiner::Copy (aiLight** _dest, const aiLight* src) } // ------------------------------------------------------------------------------------------------ -void SceneCombiner::Copy (aiBone** _dest, const aiBone* src) -{ - ai_assert(NULL != _dest && NULL != src); +void SceneCombiner::Copy(aiBone** _dest, const aiBone* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } aiBone* dest = *_dest = new aiBone(); @@ -1230,10 +1243,10 @@ void SceneCombiner::Copy (aiNode** _dest, const aiNode* src) } // ------------------------------------------------------------------------------------------------ -void SceneCombiner::Copy(aiMetadata** _dest, const aiMetadata* src) -{ - ai_assert( NULL != _dest ); - ai_assert( NULL != src); +void SceneCombiner::Copy(aiMetadata** _dest, const aiMetadata* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } aiMetadata* dest = *_dest = aiMetadata::Alloc( src->mNumProperties ); std::copy(src->mKeys, src->mKeys + src->mNumProperties, dest->mKeys); @@ -1271,4 +1284,5 @@ void SceneCombiner::Copy(aiMetadata** _dest, const aiMetadata* src) } } -} +} // Namespace Assimp + diff --git a/include/assimp/SceneCombiner.h b/include/assimp/SceneCombiner.h index ebb5dda00..aa57406b9 100644 --- a/include/assimp/SceneCombiner.h +++ b/include/assimp/SceneCombiner.h @@ -197,13 +197,17 @@ struct SceneHelper * The class is currently being used by various postprocessing steps * and loaders (ie. LWS). */ -class ASSIMP_API SceneCombiner -{ +class ASSIMP_API SceneCombiner { // class cannot be instanced - SceneCombiner() {} + SceneCombiner() { + // empty + } + + ~SceneCombiner() { + // empty + } public: - // ------------------------------------------------------------------- /** Merges two or more scenes. * diff --git a/test/unit/utSceneCombiner.cpp b/test/unit/utSceneCombiner.cpp index 3a283515e..99d483769 100644 --- a/test/unit/utSceneCombiner.cpp +++ b/test/unit/utSceneCombiner.cpp @@ -71,3 +71,8 @@ TEST_F( utSceneCombiner, MergeMeshes_ValidNames_Test ) { std::string outName = out->mName.C_Str(); EXPECT_EQ( "mesh_1.mesh_2.mesh_3", outName ); } + +TEST_F( utSceneCombiner, CopySceneWithNullptr_NoException ) { + EXPECT_NO_THROW( SceneCombiner::CopyScene( nullptr, nullptr ) ); + EXPECT_NO_THROW( SceneCombiner::CopySceneFlat( nullptr, nullptr ) ); +} From 2a9f79f9582a09d14bebccaf8f0f32e2f2dd8ab5 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 19 Nov 2017 19:05:51 +0100 Subject: [PATCH 357/490] check for 0 properties before copy them --- code/SceneCombiner.cpp | 32 +++++++++++++++----------------- include/assimp/metadata.h | 2 +- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/code/SceneCombiner.cpp b/code/SceneCombiner.cpp index 608aa8297..4b77bb2db 100644 --- a/code/SceneCombiner.cpp +++ b/code/SceneCombiner.cpp @@ -107,8 +107,9 @@ void SceneCombiner::AddNodePrefixes(aiNode* node, const char* prefix, unsigned i PrefixString(node->mName,prefix,len); // Process all children recursively - for (unsigned int i = 0; i < node->mNumChildren;++i) - AddNodePrefixes(node->mChildren[i],prefix,len); + for ( unsigned int i = 0; i < node->mNumChildren; ++i ) { + AddNodePrefixes( node->mChildren[ i ], prefix, len ); + } } // ------------------------------------------------------------------------------------------------ @@ -118,7 +119,6 @@ bool SceneCombiner::FindNameMatch(const aiString& name, std::vector // Check whether we find a positive match in one of the given sets for (unsigned int i = 0; i < input.size(); ++i) { - if (cur != i && input[i].hashes.find(hash) != input[i].hashes.end()) { return true; } @@ -135,7 +135,6 @@ void SceneCombiner::AddNodePrefixesChecked(aiNode* node, const char* prefix, uns // Check whether we find a positive match in one of the given sets for (unsigned int i = 0; i < input.size(); ++i) { - if (cur != i && input[i].hashes.find(hash) != input[i].hashes.end()) { PrefixString(node->mName,prefix,len); break; @@ -149,28 +148,25 @@ void SceneCombiner::AddNodePrefixesChecked(aiNode* node, const char* prefix, uns // ------------------------------------------------------------------------------------------------ // Add an offset to all mesh indices in a node graph -void SceneCombiner::OffsetNodeMeshIndices (aiNode* node, unsigned int offset) -{ +void SceneCombiner::OffsetNodeMeshIndices (aiNode* node, unsigned int offset) { for (unsigned int i = 0; i < node->mNumMeshes;++i) node->mMeshes[i] += offset; - for (unsigned int i = 0; i < node->mNumChildren;++i) - OffsetNodeMeshIndices(node->mChildren[i],offset); + for ( unsigned int i = 0; i < node->mNumChildren; ++i ) { + OffsetNodeMeshIndices( node->mChildren[ i ], offset ); + } } // ------------------------------------------------------------------------------------------------ // Merges two scenes. Currently only used by the LWS loader. -void SceneCombiner::MergeScenes(aiScene** _dest,std::vector& src, - unsigned int flags) { +void SceneCombiner::MergeScenes(aiScene** _dest,std::vector& src, unsigned int flags) { if ( nullptr == _dest ) { return; } // if _dest points to NULL allocate a new scene. Otherwise clear the old and reuse it - if (src.empty()) - { - if (*_dest) - { + if (src.empty()) { + if (*_dest) { (*_dest)->~aiScene(); SceneCombiner::CopySceneFlat(_dest,src[0]); } @@ -1078,8 +1074,7 @@ void SceneCombiner::Copy( aiMesh** _dest, const aiMesh* src ) { // make a deep copy of all faces GetArrayCopy(dest->mFaces,dest->mNumFaces); - for (unsigned int i = 0; i < dest->mNumFaces;++i) - { + for (unsigned int i = 0; i < dest->mNumFaces;++i) { aiFace& f = dest->mFaces[i]; GetArrayCopy(f.mIndices,f.mNumIndices); } @@ -1091,7 +1086,6 @@ void SceneCombiner::Copy (aiMaterial** _dest, const aiMaterial* src) { return; } - aiMaterial* dest = (aiMaterial*) ( *_dest = new aiMaterial() ); dest->Clear(); @@ -1248,6 +1242,10 @@ void SceneCombiner::Copy(aiMetadata** _dest, const aiMetadata* src) { return; } + if ( 0 == src->mNumProperties ) { + return; + } + aiMetadata* dest = *_dest = aiMetadata::Alloc( src->mNumProperties ); std::copy(src->mKeys, src->mKeys + src->mNumProperties, dest->mKeys); diff --git a/include/assimp/metadata.h b/include/assimp/metadata.h index ded08d14b..92db9b59a 100644 --- a/include/assimp/metadata.h +++ b/include/assimp/metadata.h @@ -187,7 +187,7 @@ struct aiMetadata { static inline aiMetadata *Alloc( unsigned int numProperties ) { if ( 0 == numProperties ) { - return NULL; + return nullptr; } aiMetadata *data = new aiMetadata; From 4fb5038fb194b7bf75a5985a2f88d0e2007d792b Mon Sep 17 00:00:00 2001 From: Mika Rautio Date: Mon, 20 Nov 2017 00:10:05 +0200 Subject: [PATCH 358/490] Add support for building Mac OS X Framework bundles --- CMakeLists.txt | 9 +++++++++ code/CMakeLists.txt | 25 +++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 73a30e9d4..0fc9af467 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,10 @@ OPTION( BUILD_SHARED_LIBS "Build package with shared libraries." ON ) +OPTION( BUILD_FRAMEWORK + "Build package as Mac OS X Framework bundle." + OFF +) OPTION( ASSIMP_DOUBLE_PRECISION "Set to ON to enable double precision processing" OFF @@ -111,6 +115,11 @@ IF(MSVC) ) ENDIF(MSVC) +IF (BUILD_FRAMEWORK) + SET (BUILD_SHARED_LIBS ON) + MESSAGE(STATUS "Framework bundle building enabled") +ENDIF(BUILD_FRAMEWORK) + IF(NOT BUILD_SHARED_LIBS) MESSAGE(STATUS "Shared libraries disabled") SET(LINK_SEARCH_START_STATIC TRUE) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 4890a3c9e..1e08bdb44 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -914,8 +914,27 @@ SET_TARGET_PROPERTIES( assimp PROPERTIES ) if (APPLE) - SET_TARGET_PROPERTIES( assimp PROPERTIES INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${ASSIMP_LIB_INSTALL_DIR}") -endif() + SET_TARGET_PROPERTIES( assimp PROPERTIES + INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${ASSIMP_LIB_INSTALL_DIR}" + ) + + if (BUILD_FRAMEWORK) + SET_TARGET_PROPERTIES( assimp PROPERTIES + FRAMEWORK TRUE + FRAMEWORK_VERSION C + MACOSX_FRAMEWORK_IDENTIFIER net.sf.assimp + PUBLIC_HEADER "${PUBLIC_HEADERS}" + ) + + # PUBLIC_HEADER option does not support directory structure creation + # add ./Compiler/*.h to assimp.framework via copy command + ADD_CUSTOM_COMMAND(TARGET assimp POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "../${HEADER_PATH}/Compiler" + assimp.framework/Headers/Compiler + COMMENT "Copying public ./Compiler/ header files to framework bundle's Headers/Compiler/") + endif(BUILD_FRAMEWORK) +endif(APPLE) # Build against external unzip, or add ../contrib/unzip so # assimp can #include "unzip.h" @@ -935,9 +954,11 @@ INSTALL( TARGETS assimp LIBRARY DESTINATION ${ASSIMP_LIB_INSTALL_DIR} ARCHIVE DESTINATION ${ASSIMP_LIB_INSTALL_DIR} RUNTIME DESTINATION ${ASSIMP_BIN_INSTALL_DIR} + FRAMEWORK DESTINATION ${ASSIMP_LIB_INSTALL_DIR} COMPONENT ${LIBASSIMP_COMPONENT}) INSTALL( FILES ${PUBLIC_HEADERS} DESTINATION ${ASSIMP_INCLUDE_INSTALL_DIR}/assimp COMPONENT assimp-dev) INSTALL( FILES ${COMPILER_HEADERS} DESTINATION ${ASSIMP_INCLUDE_INSTALL_DIR}/assimp/Compiler COMPONENT assimp-dev) + if (ASSIMP_ANDROID_JNIIOSYSTEM) INSTALL(FILES ${HEADER_PATH}/${ASSIMP_ANDROID_JNIIOSYSTEM_PATH}/AndroidJNIIOSystem.h DESTINATION ${ASSIMP_INCLUDE_INSTALL_DIR} From 6cbfd5b977486fe419dcc218a3180d88c68f024a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20D=C3=A4hne?= Date: Mon, 20 Nov 2017 14:59:05 +0100 Subject: [PATCH 359/490] [glTF2] Implemented reading binary glTF2 (glb) files --- code/glTF2Asset.h | 29 +++++++++++-- code/glTF2Asset.inl | 99 ++++++++++++++++++++++++++++++++++++++---- code/glTF2Importer.cpp | 8 ++-- 3 files changed, 120 insertions(+), 16 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 877b59ba3..0283ea0d8 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -192,15 +192,31 @@ namespace glTF2 #include "./../include/assimp/Compiler/pushpack1.h" #endif + //! For binary .glb files + //! 12-byte header (+ the JSON + a "body" data section) + struct GLB_Header + { + uint8_t magic[4]; //!< Magic number: "glTF" + uint32_t version; //!< Version number (always 2 as of the last update) + uint32_t length; //!< Total length of the Binary glTF, including header, scene, and body, in bytes + } PACK_STRUCT; + + struct GLB_Chunk + { + uint32_t chunkLength; + uint32_t chunkType; + } PACK_STRUCT; + #ifdef ASSIMP_API #include "./../include/assimp/Compiler/poppack1.h" #endif - //! Values for the GLB_Header::sceneFormat field - enum SceneFormat + //! Values for the GLB_Chunk::chunkType field + enum ChunkType { - SceneFormat_JSON = 0 + ChunkType_JSON = 0x4E4F534A, + ChunkType_BIN = 0x004E4942 }; //! Values for the mesh primitive modes @@ -1086,7 +1102,10 @@ namespace glTF2 } //! Main function - void Load(const std::string& file); + void Load(const std::string& file, bool isBinary = false); + + //! Enables binary encoding on the asset + void SetAsBinary(); //! Search for an available name, starting from the given strings std::string FindUniqueID(const std::string& str, const char* suffix); @@ -1095,6 +1114,8 @@ namespace glTF2 { return mBodyBuffer; } private: + void ReadBinaryHeader(IOStream& stream, std::vector& sceneData); + void ReadExtensionsUsed(Document& doc); IOStream* OpenFile(std::string path, const char* mode, bool absolute = false); diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 09b049add..72fdf9580 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -1037,7 +1037,72 @@ inline void AssetMetadata::Read(Document& doc) // Asset methods implementation // -inline void Asset::Load(const std::string& pFile) +inline void Asset::ReadBinaryHeader(IOStream& stream, std::vector& sceneData) +{ + GLB_Header header; + if (stream.Read(&header, sizeof(header), 1) != 1) { + throw DeadlyImportError("GLTF: Unable to read the file header"); + } + + if (strncmp((char*)header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic)) != 0) { + throw DeadlyImportError("GLTF: Invalid binary glTF file"); + } + + AI_SWAP4(header.version); + asset.version = to_string(header.version); + if (header.version != 2) { + throw DeadlyImportError("GLTF: Unsupported binary glTF version"); + } + + GLB_Chunk chunk; + if (stream.Read(&chunk, sizeof(chunk), 1) != 1) { + throw DeadlyImportError("GLTF: Unable to read JSON chunk"); + } + + AI_SWAP4(chunk.chunkLength); + AI_SWAP4(chunk.chunkType); + + if (chunk.chunkType != ChunkType_JSON) { + throw DeadlyImportError("GLTF: JSON chunk missing"); + } + + // read the scene data + + mSceneLength = chunk.chunkLength; + sceneData.resize(mSceneLength + 1); + sceneData[mSceneLength] = '\0'; + + if (stream.Read(&sceneData[0], 1, mSceneLength) != mSceneLength) { + throw DeadlyImportError("GLTF: Could not read the file contents"); + } + + uint32_t padding = ((chunk.chunkLength + 3) & ~3) - chunk.chunkLength; + if (padding > 0) { + stream.Seek(padding, aiOrigin_CUR); + } + + AI_SWAP4(header.length); + mBodyOffset = 12 + 8 + chunk.chunkLength + padding + 8; + if (header.length >= mBodyOffset) { + if (stream.Read(&chunk, sizeof(chunk), 1) != 1) { + throw DeadlyImportError("GLTF: Unable to read BIN chunk"); + } + + AI_SWAP4(chunk.chunkLength); + AI_SWAP4(chunk.chunkType); + + if (chunk.chunkType != ChunkType_BIN) { + throw DeadlyImportError("GLTF: BIN chunk missing"); + } + + mBodyLength = chunk.chunkLength; + } + else { + mBodyOffset = mBodyLength = 0; + } +} + +inline void Asset::Load(const std::string& pFile, bool isBinary) { mCurrentAssetDir.clear(); int pos = std::max(int(pFile.rfind('/')), int(pFile.rfind('\\'))); @@ -1048,16 +1113,25 @@ inline void Asset::Load(const std::string& pFile) throw DeadlyImportError("GLTF: Could not open file for reading"); } - mSceneLength = stream->FileSize(); - mBodyLength = 0; + // is binary? then read the header + std::vector sceneData; + if (isBinary) { + SetAsBinary(); // also creates the body buffer + ReadBinaryHeader(*stream, sceneData); + } + else { + mSceneLength = stream->FileSize(); + mBodyLength = 0; - // read the scene data - std::vector sceneData(mSceneLength + 1); - sceneData[mSceneLength] = '\0'; + // read the scene data - if (stream->Read(&sceneData[0], 1, mSceneLength) != mSceneLength) { - throw DeadlyImportError("GLTF: Could not read the file contents"); + sceneData.resize(mSceneLength + 1); + sceneData[mSceneLength] = '\0'; + + if (stream->Read(&sceneData[0], 1, mSceneLength) != mSceneLength) { + throw DeadlyImportError("GLTF: Could not read the file contents"); + } } @@ -1110,6 +1184,15 @@ inline void Asset::Load(const std::string& pFile) } } +inline void Asset::SetAsBinary() +{ + if (!mBodyBuffer) { + mBodyBuffer = buffers.Create("binary_glTF"); + mBodyBuffer->MarkAsSpecial(); + } +} + + inline void Asset::ReadExtensionsUsed(Document& doc) { Value* extsUsed = FindArray(doc, "extensionsUsed"); diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 5d40d75bc..297f2bc72 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -74,7 +74,7 @@ static const aiImporterDesc desc = { "", "", "", - aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental, + aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental, 0, 0, 0, @@ -103,13 +103,13 @@ bool glTF2Importer::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool { const std::string &extension = GetExtension(pFile); - if (extension != "gltf") // We currently can't read glTF2 binary files (.glb), yet + if (extension != "gltf" && extension != "glb") return false; if (checkSig && pIOHandler) { glTF2::Asset asset(pIOHandler); try { - asset.Load(pFile); + asset.Load(pFile, extension == "glb"); std::string version = asset.asset.version; return !version.empty() && version[0] == '2'; } catch (...) { @@ -639,7 +639,7 @@ void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IO // read the asset file glTF2::Asset asset(pIOHandler); - asset.Load(pFile); + asset.Load(pFile, GetExtension(pFile) == "glb"); // // Copy the data out From a05d74a281dd786b41e5ef9daf3c0ebea1988797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20D=C3=A4hne?= Date: Mon, 20 Nov 2017 19:01:28 +0100 Subject: [PATCH 360/490] [glTF2] Moved byteStride from accessor to bufferView --- code/glTF2Asset.h | 2 +- code/glTF2Asset.inl | 6 +++--- code/glTF2AssetWriter.inl | 7 +++---- code/glTF2Exporter.cpp | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 0283ea0d8..765f5e0f8 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -436,7 +436,6 @@ namespace glTF2 { Ref bufferView; //!< The ID of the bufferView. (required) unsigned int byteOffset; //!< The offset relative to the start of the bufferView in bytes. (required) - unsigned int byteStride; //!< The stride, in bytes, between attributes referenced by this accessor. (default: 0) ComponentType componentType; //!< The datatype of components in the attribute. (required) unsigned int count; //!< The number of attributes referenced by this accessor. (required) AttribType::Value type; //!< Specifies if the attribute is a scalar, vector, or matrix. (required) @@ -627,6 +626,7 @@ namespace glTF2 Ref buffer; //! The ID of the buffer. (required) size_t byteOffset; //! The offset into the buffer in bytes. (required) size_t byteLength; //! The length of the bufferView in bytes. (default: 0) + unsigned int byteStride; //!< The stride, in bytes, between attributes referenced by this accessor. (default: 0) BufferViewTarget target; //! The target that the WebGL buffer should be bound to. diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 72fdf9580..ca18e87e0 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -512,6 +512,7 @@ inline void BufferView::Read(Value& obj, Asset& r) byteOffset = MemberOrDefault(obj, "byteOffset", 0u); byteLength = MemberOrDefault(obj, "byteLength", 0u); + byteStride = MemberOrDefault(obj, "byteStride", 0u); } // @@ -526,7 +527,6 @@ inline void Accessor::Read(Value& obj, Asset& r) } byteOffset = MemberOrDefault(obj, "byteOffset", 0u); - byteStride = MemberOrDefault(obj, "byteStride", 0u); componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE); count = MemberOrDefault(obj, "count", 0u); @@ -601,7 +601,7 @@ bool Accessor::ExtractData(T*& outData) const size_t elemSize = GetElementSize(); const size_t totalSize = elemSize * count; - const size_t stride = byteStride ? byteStride : elemSize; + const size_t stride = bufferView && bufferView->byteStride ? bufferView->byteStride : elemSize; const size_t targetElemSize = sizeof(T); ai_assert(elemSize <= targetElemSize); @@ -641,7 +641,7 @@ inline Accessor::Indexer::Indexer(Accessor& acc) : accessor(acc) , data(acc.GetPointer()) , elemSize(acc.GetElementSize()) - , stride(acc.byteStride ? acc.byteStride : elemSize) + , stride(acc.bufferView && acc.bufferView->byteStride ? acc.bufferView->byteStride : elemSize) { } diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index 8583462ce..8b2769a37 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -98,10 +98,6 @@ namespace glTF2 { obj.AddMember("bufferView", a.bufferView->index, w.mAl); obj.AddMember("byteOffset", a.byteOffset, w.mAl); - if (a.byteStride != 0) { - obj.AddMember("byteStride", a.byteStride, w.mAl); - } - obj.AddMember("componentType", int(a.componentType), w.mAl); obj.AddMember("count", a.count, w.mAl); obj.AddMember("type", StringRef(AttribType::ToString(a.type)), w.mAl); @@ -168,6 +164,9 @@ namespace glTF2 { obj.AddMember("buffer", bv.buffer->index, w.mAl); obj.AddMember("byteOffset", static_cast(bv.byteOffset), w.mAl); obj.AddMember("byteLength", static_cast(bv.byteLength), w.mAl); + if (bv.byteStride != 0) { + obj.AddMember("byteStride", bv.byteStride, w.mAl); + } obj.AddMember("target", int(bv.target), w.mAl); } diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index ed6814b7f..e6d4d1a3b 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -170,13 +170,13 @@ inline Ref ExportData(Asset& a, std::string& meshName, Ref& bu bv->buffer = buffer; bv->byteOffset = unsigned(offset); bv->byteLength = length; //! The target that the WebGL buffer should be bound to. + bv->byteStride = 0; bv->target = isIndices ? BufferViewTarget_ELEMENT_ARRAY_BUFFER : BufferViewTarget_ARRAY_BUFFER; // accessor Ref acc = a.accessors.Create(a.FindUniqueID(meshName, "accessor")); acc->bufferView = bv; acc->byteOffset = 0; - acc->byteStride = 0; acc->componentType = compType; acc->count = count; acc->type = typeOut; From c22b4acd4731950efb80f2d94e647cefadfc9b74 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 20 Nov 2017 22:36:17 +0100 Subject: [PATCH 361/490] 3MF: Export initial commit. --- code/3DSExporter.cpp | 6 ++ code/3DSExporter.h | 17 ++-- code/3MFXmlTags.h | 73 ++++++++++++++++ code/CMakeLists.txt | 3 + code/D3MFExporter.cpp | 181 ++++++++++++++++++++++++++++++++++++++++ code/D3MFExporter.h | 83 ++++++++++++++++++ code/D3MFImporter.cpp | 78 ++++++----------- code/D3MFImporter.h | 12 ++- code/D3MFOpcPackage.cpp | 45 +++------- code/D3MFOpcPackage.h | 8 +- code/irrXMLWrapper.h | 4 +- 11 files changed, 397 insertions(+), 113 deletions(-) create mode 100644 code/3MFXmlTags.h create mode 100644 code/D3MFExporter.cpp create mode 100644 code/D3MFExporter.h diff --git a/code/3DSExporter.cpp b/code/3DSExporter.cpp index 7b937d878..91c241ef7 100644 --- a/code/3DSExporter.cpp +++ b/code/3DSExporter.cpp @@ -210,6 +210,12 @@ Discreet3DSExporter:: Discreet3DSExporter(std::shared_ptr outfile, con } } +// ------------------------------------------------------------------------------------------------ +Discreet3DSExporter::~Discreet3DSExporter() { + // empty +} + + // ------------------------------------------------------------------------------------------------ int Discreet3DSExporter::WriteHierarchy(const aiNode& node, int seq, int sibling_level) { diff --git a/code/3DSExporter.h b/code/3DSExporter.h index dd3c4d427..fc02e2390 100644 --- a/code/3DSExporter.h +++ b/code/3DSExporter.h @@ -60,23 +60,21 @@ namespace Assimp { // ------------------------------------------------------------------------------------------------ -/** Helper class to export a given scene to a 3DS file. */ +/** + * @brief Helper class to export a given scene to a 3DS file. + */ // ------------------------------------------------------------------------------------------------ -class Discreet3DSExporter -{ +class Discreet3DSExporter { public: Discreet3DSExporter(std::shared_ptr outfile, const aiScene* pScene); + ~Discreet3DSExporter(); private: - void WriteMeshes(); void WriteMaterials(); void WriteTexture(const aiMaterial& mat, aiTextureType type, uint16_t chunk_flags); - void WriteFaceMaterialChunk(const aiMesh& mesh); - int WriteHierarchy(const aiNode& node, int level, int sibling_level); - void WriteString(const std::string& s); void WriteString(const aiString& s); void WriteColor(const aiColor3D& color); @@ -84,7 +82,6 @@ private: void WritePercentChunk(double f); private: - const aiScene* const scene; StreamWriterLE writer; @@ -95,6 +92,6 @@ private: }; -} +} // Namespace Assimp -#endif +#endif // AI_3DSEXPORTER_H_INC diff --git a/code/3MFXmlTags.h b/code/3MFXmlTags.h new file mode 100644 index 000000000..73d151aa0 --- /dev/null +++ b/code/3MFXmlTags.h @@ -0,0 +1,73 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#pragma once + +namespace Assimp { +namespace D3MF { + +namespace XmlTag { + static const std::string model = "model"; + static const std::string model_unit = "unit"; + static const std::string metadata = "metadata"; + static const std::string resources = "resources"; + static const std::string object = "object"; + static const std::string mesh = "mesh"; + static const std::string vertices = "vertices"; + static const std::string vertex = "vertex"; + static const std::string triangles = "triangles"; + static const std::string triangle = "triangle"; + static const std::string x = "x"; + static const std::string y = "y"; + static const std::string z = "z"; + static const std::string v1 = "v1"; + static const std::string v2 = "v2"; + static const std::string v3 = "v3"; + static const std::string id = "id"; + static const std::string name = "name"; + static const std::string type = "type"; + static const std::string build = "build"; + static const std::string item = "item"; + static const std::string objectid = "objectid"; + static const std::string transform = "transform"; +} + +} +} diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 4890a3c9e..9b3b96618 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -677,8 +677,11 @@ ADD_ASSIMP_IMPORTER( GLTF ADD_ASSIMP_IMPORTER( 3MF D3MFImporter.h D3MFImporter.cpp + D3MFExporter.h + D3MFExporter.cpp D3MFOpcPackage.h D3MFOpcPackage.cpp + 3MFXmlTags.h ) ADD_ASSIMP_IMPORTER( MMD diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp new file mode 100644 index 000000000..6705ed768 --- /dev/null +++ b/code/D3MFExporter.cpp @@ -0,0 +1,181 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#include "D3MFExporter.h" +#include +#include +#include +#include + +#include + +#include "Exceptional.h" +#include "3MFXmlTags.h" + +namespace Assimp { +namespace D3MF { + +void ExportScene3MF( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/ ) { + std::shared_ptr outfile( pIOSystem->Open( pFile, "wb" ) ); + if ( !outfile ) { + throw DeadlyExportError( "Could not open output .3ds file: " + std::string( pFile ) ); + } + + D3MFExporter myExporter( outfile, pScene ); + if ( myExporter.validate() ) { + bool ok = myExporter.exportAsset(); + } +} + +D3MFExporter::D3MFExporter( std::shared_ptr outfile, const aiScene* pScene ) +: mStream( outfile.get() ) +, mScene( pScene ) +, mBuildItems() { + // empty +} + +D3MFExporter::~D3MFExporter() { + // empty +} + +bool D3MFExporter::validate() { + if ( nullptr == mStream ) { + return false; + } + + if ( nullptr == mScene ) { + return false; + } + + return true; +} + +bool D3MFExporter::exportAsset() { + writeHeader(); + mOutput << "<" << XmlTag::model << " " << XmlTag::model_unit << "=\"millimeter\"" + << "xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\">" + << "\n"; + mOutput << "<" << XmlTag::resources << ">\n"; + + writeObjects(); + + mOutput << "\n"; + writeBuild(); + + mOutput << "\n"; +} + +void D3MFExporter::writeHeader() { + mOutput << "" << "\n"; +} + +void D3MFExporter::writeObjects() { + if ( nullptr == mScene->mRootNode ) { + return; + } + + aiNode *root = mScene->mRootNode; + for ( unsigned int i = 0; i < root->mNumChildren; ++i ) { + aiNode *currentNode( root->mChildren[ i ] ); + if ( nullptr == currentNode ) { + continue; + } + mOutput << "<" << XmlTag::object << " id=\"" << currentNode->mName.C_Str() << "\" type=\"model\">\n"; + for ( unsigned int j = 0; j < currentNode->mNumMeshes; ++j ) { + aiMesh *currentMesh = mScene->mMeshes[ currentNode->mMeshes[ j ] ]; + if ( nullptr == currentMesh ) { + continue; + } + writeMesh( currentMesh ); + } + mBuildItems.push_back( i ); + + mOutput << "\n"; + } +} + +void D3MFExporter::writeMesh( aiMesh *mesh ) { + if ( nullptr == mesh ) { + return; + } + + mOutput << "<" << XmlTag::mesh << ">\n"; + mOutput << "<" << XmlTag::vertices << ">\n"; + for ( unsigned int i = 0; i < mesh->mNumVertices; ++i ) { + writeVertex( mesh->mVertices[ i ] ); + } + mOutput << "\n"; + mOutput << "\n"; + + writeFaces( mesh ); +} + +void D3MFExporter::writeVertex( const aiVector3D &pos ) { + mOutput << "<" << XmlTag::vertex << " x=\"" << pos.x << "\" y=\"" << pos.y << "\" z=\"" << pos.z << "\">\n"; +} + +void D3MFExporter::writeFaces( aiMesh *mesh ) { + if ( nullptr == mesh ) { + return; + } + + if ( !mesh->HasFaces() ) { + return; + } + mOutput << "<" << XmlTag::triangles << ">\n"; + for ( unsigned int i = 0; i < mesh->mNumFaces; ++i ) { + aiFace ¤tFace = mesh->mFaces[ i ]; + mOutput << "<" << XmlTag::triangle << " v1=\"" << currentFace.mIndices[ 0 ] << "\" v2=\"" + << currentFace.mIndices[ 1 ] << "\" v3=\"" << currentFace.mIndices[ 2 ] << "\"/>\n"; + } + mOutput << "\n"; +} + +void D3MFExporter::writeBuild() { + mOutput << "<" << XmlTag::build << ">\n"; + + for ( size_t i = 0; i < mBuildItems.size(); ++i ) { + mOutput << "<" << XmlTag::item << " objectid=\"" << i + 1 << "\"/>\n"; + } + mOutput << "\n"; +} + +} +} // Namespace Assimp diff --git a/code/D3MFExporter.h b/code/D3MFExporter.h new file mode 100644 index 000000000..3d3a633c9 --- /dev/null +++ b/code/D3MFExporter.h @@ -0,0 +1,83 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#pragma once + +#include +#include +#include +#include + +struct aiScene; +struct aiNode; +struct aiMaterial; +struct aiMesh; + +namespace Assimp { + +class IOStream; + +namespace D3MF { + +class D3MFExporter { +public: + D3MFExporter( std::shared_ptr outfile, const aiScene* pScene ); + ~D3MFExporter(); + bool validate(); + bool exportAsset(); + +protected: + void writeHeader(); + void writeObjects(); + void writeMesh( aiMesh *mesh ); + void writeVertex( const aiVector3D &pos ); + void writeFaces( aiMesh *mesh ); + void writeBuild(); + +private: + IOStream *mStream; + const aiScene *mScene; + std::ostringstream mOutput; + std::vector mBuildItems; +}; + +} +} // Namespace Assimp + diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index 6d61a0669..6f77706f9 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -59,45 +59,22 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "D3MFOpcPackage.h" #include #include "irrXMLWrapper.h" +#include "3MFXmlTags.h" namespace Assimp { namespace D3MF { -namespace XmlTag { - static const std::string model = "model"; - static const std::string metadata = "metadata"; - static const std::string resources = "resources"; - static const std::string object = "object"; - static const std::string mesh = "mesh"; - static const std::string vertices = "vertices"; - static const std::string vertex = "vertex"; - static const std::string triangles = "triangles"; - static const std::string triangle = "triangle"; - static const std::string x = "x"; - static const std::string y = "y"; - static const std::string z = "z"; - static const std::string v1 = "v1"; - static const std::string v2 = "v2"; - static const std::string v3 = "v3"; - static const std::string id = "id"; - static const std::string name = "name"; - static const std::string type = "type"; - static const std::string build = "build"; - static const std::string item = "item"; - static const std::string objectid = "objectid"; - static const std::string transform = "transform"; -} - - -class XmlSerializer -{ +class XmlSerializer { public: XmlSerializer(XmlReader* xmlReader) - : xmlReader(xmlReader) - { + : xmlReader(xmlReader) { // empty } + ~XmlSerializer() { + + } + void ImportXml(aiScene* scene) { scene->mRootNode = new aiNode(); std::vector children; @@ -115,8 +92,9 @@ public: } } - if(scene->mRootNode->mName.length == 0) - scene->mRootNode->mName.Set("3MF"); + if ( scene->mRootNode->mName.length == 0 ) { + scene->mRootNode->mName.Set( "3MF" ); + } scene->mNumMeshes = static_cast(meshes.size()); @@ -178,10 +156,8 @@ private: } - aiMesh* ReadMesh() - { + aiMesh* ReadMesh() { aiMesh* mesh = new aiMesh(); - while(ReadToEndElement(D3MF::XmlTag::mesh)) { if(xmlReader->getNodeName() == D3MF::XmlTag::vertices) @@ -192,10 +168,8 @@ private: { ImportTriangles(mesh); } - } - return mesh; } @@ -216,6 +190,7 @@ private: std::copy(vertices.begin(), vertices.end(), mesh->mVertices); } + aiVector3D ReadVertex() { aiVector3D vertex; @@ -261,7 +236,6 @@ private: } private: - bool ReadToStartElement(const std::string& startTag) { while(xmlReader->read()) @@ -305,6 +279,7 @@ private: } //namespace D3MF +static const std::string Extension = "3mf"; static const aiImporterDesc desc = { "3mf Importer", @@ -316,24 +291,22 @@ static const aiImporterDesc desc = { 0, 0, 0, - "3mf" + Extension.c_str() }; D3MFImporter::D3MFImporter() -{ - +: BaseImporter() { + // empty } -D3MFImporter::~D3MFImporter() -{ - +D3MFImporter::~D3MFImporter() { + // empty } -bool D3MFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const -{ +bool D3MFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { const std::string extension = GetExtension(pFile); - if(extension == "3mf") { + if(extension == Extension ) { return true; } else if ( !extension.length() || checkSig ) { if (nullptr == pIOHandler ) { @@ -344,18 +317,15 @@ bool D3MFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool return false; } -void D3MFImporter::SetupProperties(const Importer */*pImp*/) -{ - +void D3MFImporter::SetupProperties(const Importer * /*pImp*/) { + // empty } -const aiImporterDesc *D3MFImporter::GetInfo() const -{ +const aiImporterDesc *D3MFImporter::GetInfo() const { return &desc; } -void D3MFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) -{ +void D3MFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { D3MF::D3MFOpcPackage opcPackage(pIOHandler, pFile); std::unique_ptr xmlStream(new CIrrXML_IOStreamReader(opcPackage.RootStream())); diff --git a/code/D3MFImporter.h b/code/D3MFImporter.h index fb65d8606..604d73bd5 100644 --- a/code/D3MFImporter.h +++ b/code/D3MFImporter.h @@ -46,21 +46,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { -class D3MFImporter : public BaseImporter -{ +class D3MFImporter : public BaseImporter { public: + // BaseImporter interface D3MFImporter(); ~D3MFImporter(); - - // BaseImporter interface -public: bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const; void SetupProperties(const Importer *pImp); const aiImporterDesc *GetInfo() const; protected: void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler); - }; -} + +} // Namespace Assimp + #endif // AI_D3MFLOADER_H_INCLUDED diff --git a/code/D3MFOpcPackage.cpp b/code/D3MFOpcPackage.cpp index 03c39a29d..42f58cb68 100644 --- a/code/D3MFOpcPackage.cpp +++ b/code/D3MFOpcPackage.cpp @@ -80,24 +80,15 @@ namespace XmlTag { } class IOSystem2Unzip { - - public: - - static voidpf open(voidpf opaque, const char* filename, int mode); - - static uLong read(voidpf opaque, voidpf stream, void* buf, uLong size); - - static uLong write(voidpf opaque, voidpf stream, const void* buf, uLong size); - - static long tell(voidpf opaque, voidpf stream); - - static long seek(voidpf opaque, voidpf stream, uLong offset, int origin); - - static int close(voidpf opaque, voidpf stream); - - static int testerror(voidpf opaque, voidpf stream); - - static zlib_filefunc_def get(IOSystem* pIOHandler); +public: + static voidpf open(voidpf opaque, const char* filename, int mode); + static uLong read(voidpf opaque, voidpf stream, void* buf, uLong size); + static uLong write(voidpf opaque, voidpf stream, const void* buf, uLong size); + static long tell(voidpf opaque, voidpf stream); + static long seek(voidpf opaque, voidpf stream, uLong offset, int origin); + static int close(voidpf opaque, voidpf stream); + static int testerror(voidpf opaque, voidpf stream); + static zlib_filefunc_def get(IOSystem* pIOHandler); }; voidpf IOSystem2Unzip::open(voidpf opaque, const char* filename, int mode) { @@ -257,43 +248,27 @@ void ZipFile::Flush() { } -class D3MFZipArchive : public IOSystem -{ +class D3MFZipArchive : public IOSystem { public: - static const unsigned int FileNameSize = 256; -public: - D3MFZipArchive(IOSystem* pIOHandler, const std::string & rFile); - ~D3MFZipArchive(); - bool Exists(const char* pFile) const; - char getOsSeparator() const; - IOStream* Open(const char* pFile, const char* pMode = "rb"); - void Close(IOStream* pFile); - bool isOpen() const; - void getFileList(std::vector &rFileList); private: - bool mapArchive(); private: - unzFile m_ZipFileHandle; - std::map m_ArchiveMap; - }; - // ------------------------------------------------------------------------------------------------ // Constructor. D3MFZipArchive::D3MFZipArchive(IOSystem* pIOHandler, const std::string& rFile) diff --git a/code/D3MFOpcPackage.h b/code/D3MFOpcPackage.h index e46eb7636..9ac9ade58 100644 --- a/code/D3MFOpcPackage.h +++ b/code/D3MFOpcPackage.h @@ -56,15 +56,15 @@ typedef std::shared_ptr XmlReaderPtr; class D3MFZipArchive; -class D3MFOpcPackage -{ +class D3MFOpcPackage { public: D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile); ~D3MFOpcPackage(); - IOStream* RootStream() const; -private: + +protected: std::string ReadPackageRootRelationship(IOStream* stream); + private: IOStream* m_RootStream; std::unique_ptr zipArchive; diff --git a/code/irrXMLWrapper.h b/code/irrXMLWrapper.h index d7c76bf87..bbda7c0f0 100644 --- a/code/irrXMLWrapper.h +++ b/code/irrXMLWrapper.h @@ -70,9 +70,7 @@ namespace Assimp { * } * @endcode **/ -class CIrrXML_IOStreamReader - : public irr::io::IFileReadCallBack -{ +class CIrrXML_IOStreamReader : public irr::io::IFileReadCallBack { public: // ---------------------------------------------------------------------------------- From a7be5b527f77941de60747abce5dab86aa2af646 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 20 Nov 2017 23:53:53 +0100 Subject: [PATCH 362/490] add missing return statement. --- code/D3MFExporter.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index 6705ed768..a602ff7d8 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -100,6 +100,8 @@ bool D3MFExporter::exportAsset() { writeBuild(); mOutput << "\n"; + + return true; } void D3MFExporter::writeHeader() { From 127705d347b97d21d66252daea447eca7c7c6129 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 21 Nov 2017 18:04:22 +0200 Subject: [PATCH 363/490] Unit tests: Enable data structure validation in cases where it doesn't cause failures --- test/unit/ut3DImportExport.cpp | 3 ++- test/unit/ut3DSImportExport.cpp | 3 ++- test/unit/utACImportExport.cpp | 3 ++- test/unit/utAMFImportExport.cpp | 3 ++- test/unit/utASEImportExport.cpp | 3 ++- test/unit/utB3DImportExport.cpp | 3 ++- test/unit/utBVHImportExport.cpp | 3 ++- test/unit/utBlendImportAreaLight.cpp | 3 ++- test/unit/utBlendImportMaterials.cpp | 3 ++- test/unit/utBlenderImportExport.cpp | 3 ++- test/unit/utCSMImportExport.cpp | 3 ++- test/unit/utColladaExportCamera.cpp | 5 +++-- test/unit/utColladaExportLight.cpp | 5 +++-- test/unit/utColladaImportExport.cpp | 3 ++- test/unit/utD3MFImportExport.cpp | 3 ++- test/unit/utDXFImporterExporter.cpp | 3 ++- test/unit/utExport.cpp | 4 ++-- test/unit/utFBXImporterExporter.cpp | 3 ++- test/unit/utHMPImportExport.cpp | 3 ++- test/unit/utIFCImportExport.cpp | 3 ++- test/unit/utIssues.cpp | 3 ++- test/unit/utObjImportExport.cpp | 6 +++--- test/unit/utPMXImporter.cpp | 2 +- test/unit/utQ3DImportExport.cpp | 3 ++- test/unit/utSIBImporter.cpp | 3 ++- test/unit/utSMDImportExport.cpp | 5 +++-- test/unit/utSTLImportExport.cpp | 5 +++-- test/unit/utX3DImportExport.cpp | 3 ++- test/unit/utXImporterExporter.cpp | 3 ++- test/unit/utglTF2ImportExport.cpp | 5 +++-- test/unit/utglTFImportExport.cpp | 3 ++- 31 files changed, 67 insertions(+), 39 deletions(-) diff --git a/test/unit/ut3DImportExport.cpp b/test/unit/ut3DImportExport.cpp index b55bf9c4c..6becbd866 100644 --- a/test/unit/ut3DImportExport.cpp +++ b/test/unit/ut3DImportExport.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -52,7 +53,7 @@ class ut3DImportExport : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/3D/box_a.3d", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/3D/box_a.3d", aiProcess_ValidateDataStructure ); return nullptr != scene; } }; diff --git a/test/unit/ut3DSImportExport.cpp b/test/unit/ut3DSImportExport.cpp index 1e9bfe0d0..69e889ae6 100644 --- a/test/unit/ut3DSImportExport.cpp +++ b/test/unit/ut3DSImportExport.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -52,7 +53,7 @@ class ut3DSImportExport : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/3DS/fels.3ds", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/3DS/fels.3ds", aiProcess_ValidateDataStructure ); #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER return nullptr != scene; #else diff --git a/test/unit/utACImportExport.cpp b/test/unit/utACImportExport.cpp index 3c3dcaa7b..178816532 100644 --- a/test/unit/utACImportExport.cpp +++ b/test/unit/utACImportExport.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -52,7 +53,7 @@ class utACImportExport : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/AC/Wuson.ac", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/AC/Wuson.ac", aiProcess_ValidateDataStructure ); return nullptr != scene; } }; diff --git a/test/unit/utAMFImportExport.cpp b/test/unit/utAMFImportExport.cpp index e4ee5605a..35afd375c 100644 --- a/test/unit/utAMFImportExport.cpp +++ b/test/unit/utAMFImportExport.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -52,7 +53,7 @@ class utAMFImportExport : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/AMF/test1.amf", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/AMF/test1.amf", aiProcess_ValidateDataStructure ); return nullptr != scene; } }; diff --git a/test/unit/utASEImportExport.cpp b/test/unit/utASEImportExport.cpp index 841aff549..1f62d3385 100644 --- a/test/unit/utASEImportExport.cpp +++ b/test/unit/utASEImportExport.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -52,7 +53,7 @@ class utASEImportExport : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/ASE/ThreeCubesGreen.ASE", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/ASE/ThreeCubesGreen.ASE", aiProcess_ValidateDataStructure ); #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER return nullptr != scene; #else diff --git a/test/unit/utB3DImportExport.cpp b/test/unit/utB3DImportExport.cpp index 0a800ccb6..eaa56beed 100644 --- a/test/unit/utB3DImportExport.cpp +++ b/test/unit/utB3DImportExport.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -52,7 +53,7 @@ class utB3DImportExport : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/B3D/WusonBlitz.b3d", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/B3D/WusonBlitz.b3d", aiProcess_ValidateDataStructure ); return nullptr != scene; } }; diff --git a/test/unit/utBVHImportExport.cpp b/test/unit/utBVHImportExport.cpp index 48281c667..a080ca3a4 100644 --- a/test/unit/utBVHImportExport.cpp +++ b/test/unit/utBVHImportExport.cpp @@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -50,7 +51,7 @@ class utBVHImportExport : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/BVH/01_01.bvh", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/BVH/01_01.bvh", aiProcess_ValidateDataStructure ); return nullptr != scene; } }; diff --git a/test/unit/utBlendImportAreaLight.cpp b/test/unit/utBlendImportAreaLight.cpp index 04cc8cbcf..55a68b040 100644 --- a/test/unit/utBlendImportAreaLight.cpp +++ b/test/unit/utBlendImportAreaLight.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include class BlendImportAreaLight : public ::testing::Test { public: @@ -67,7 +68,7 @@ protected: // ------------------------------------------------------------------------------------------------ TEST_F(BlendImportAreaLight, testImportLight) { - const aiScene* pTest = im->ReadFile(ASSIMP_TEST_MODELS_DIR "/BLEND/AreaLight_269.blend",0); + const aiScene* pTest = im->ReadFile(ASSIMP_TEST_MODELS_DIR "/BLEND/AreaLight_269.blend", aiProcess_ValidateDataStructure); ASSERT_TRUE(pTest != NULL); ASSERT_TRUE(pTest->HasLights()); diff --git a/test/unit/utBlendImportMaterials.cpp b/test/unit/utBlendImportMaterials.cpp index 6e1033453..4387243c4 100644 --- a/test/unit/utBlendImportMaterials.cpp +++ b/test/unit/utBlendImportMaterials.cpp @@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include class BlendImportMaterials : public ::testing::Test { public: @@ -66,7 +67,7 @@ protected: // ------------------------------------------------------------------------------------------------ TEST_F(BlendImportMaterials, testImportMaterial) { - const aiScene* pTest = im->ReadFile(ASSIMP_TEST_MODELS_DIR "/BLEND/BlenderMaterial_269.blend", 0); + const aiScene* pTest = im->ReadFile(ASSIMP_TEST_MODELS_DIR "/BLEND/BlenderMaterial_269.blend", aiProcess_ValidateDataStructure); ASSERT_TRUE(pTest != NULL); ASSERT_TRUE(pTest->HasMaterials()); diff --git a/test/unit/utBlenderImportExport.cpp b/test/unit/utBlenderImportExport.cpp index a120fdcc5..6d7ca8dd7 100644 --- a/test/unit/utBlenderImportExport.cpp +++ b/test/unit/utBlenderImportExport.cpp @@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -50,7 +51,7 @@ class utBlenderImporterExporter : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/BLEND/box.blend", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/BLEND/box.blend", aiProcess_ValidateDataStructure ); return nullptr != scene; } }; diff --git a/test/unit/utCSMImportExport.cpp b/test/unit/utCSMImportExport.cpp index eb6672a44..74cdfcf94 100644 --- a/test/unit/utCSMImportExport.cpp +++ b/test/unit/utCSMImportExport.cpp @@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -50,7 +51,7 @@ class utCSMImportExport : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/CSM/ThomasFechten.csm", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/CSM/ThomasFechten.csm", aiProcess_ValidateDataStructure ); return nullptr != scene; } }; diff --git a/test/unit/utColladaExportCamera.cpp b/test/unit/utColladaExportCamera.cpp index 4949efc83..cbb491204 100644 --- a/test/unit/utColladaExportCamera.cpp +++ b/test/unit/utColladaExportCamera.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #ifndef ASSIMP_BUILD_NO_EXPORT @@ -73,7 +74,7 @@ TEST_F(ColladaExportCamera, testExportCamera) { const char* file = "cameraExp.dae"; - const aiScene* pTest = im->ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/cameras.dae",0); + const aiScene* pTest = im->ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/cameras.dae", aiProcess_ValidateDataStructure); ASSERT_TRUE(pTest!=NULL); ASSERT_TRUE(pTest->HasCameras()); @@ -95,7 +96,7 @@ TEST_F(ColladaExportCamera, testExportCamera) names[ i ] = orig->mName; pos[ i ] = orig->mPosition; } - const aiScene* imported = im->ReadFile(file,0); + const aiScene* imported = im->ReadFile(file, aiProcess_ValidateDataStructure); ASSERT_TRUE(imported!=NULL); diff --git a/test/unit/utColladaExportLight.cpp b/test/unit/utColladaExportLight.cpp index ccff1a31e..5c4801d32 100644 --- a/test/unit/utColladaExportLight.cpp +++ b/test/unit/utColladaExportLight.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #ifndef ASSIMP_BUILD_NO_EXPORT @@ -72,7 +73,7 @@ TEST_F(ColladaExportLight, testExportLight) { const char* file = "lightsExp.dae"; - const aiScene* pTest = im->ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/lights.dae",0); + const aiScene* pTest = im->ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/lights.dae", aiProcess_ValidateDataStructure); ASSERT_TRUE(pTest!=NULL); ASSERT_TRUE(pTest->HasLights()); @@ -86,7 +87,7 @@ TEST_F(ColladaExportLight, testExportLight) EXPECT_EQ(AI_SUCCESS,ex->Export(pTest,"collada",file)); - const aiScene* imported = im->ReadFile(file,0); + const aiScene* imported = im->ReadFile(file, aiProcess_ValidateDataStructure); ASSERT_TRUE(imported!=NULL); diff --git a/test/unit/utColladaImportExport.cpp b/test/unit/utColladaImportExport.cpp index 997fca188..97cb5c2ca 100644 --- a/test/unit/utColladaImportExport.cpp +++ b/test/unit/utColladaImportExport.cpp @@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -50,7 +51,7 @@ class utColladaImportExport : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/Collada/duck.dae", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/Collada/duck.dae", aiProcess_ValidateDataStructure ); return nullptr != scene; } }; diff --git a/test/unit/utD3MFImportExport.cpp b/test/unit/utD3MFImportExport.cpp index 0c322420c..5c155da4b 100644 --- a/test/unit/utD3MFImportExport.cpp +++ b/test/unit/utD3MFImportExport.cpp @@ -44,12 +44,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include class utD3MFImporterExporter : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/3MF/box.3mf", 0); + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/3MF/box.3mf", aiProcess_ValidateDataStructure); EXPECT_EQ( 1u, scene->mNumMeshes ); aiMesh *mesh = scene->mMeshes[ 0 ]; EXPECT_NE( nullptr, mesh ); diff --git a/test/unit/utDXFImporterExporter.cpp b/test/unit/utDXFImporterExporter.cpp index 389284608..b75db3ec4 100644 --- a/test/unit/utDXFImporterExporter.cpp +++ b/test/unit/utDXFImporterExporter.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -52,7 +53,7 @@ class utDXFImporterExporter : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/DXF/PinkEggFromLW.dxf", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/DXF/PinkEggFromLW.dxf", aiProcess_ValidateDataStructure ); return nullptr != scene; } }; diff --git a/test/unit/utExport.cpp b/test/unit/utExport.cpp index f810e3dce..b3ab5e372 100644 --- a/test/unit/utExport.cpp +++ b/test/unit/utExport.cpp @@ -14,7 +14,7 @@ public: ex = new Assimp::Exporter(); im = new Assimp::Importer(); - pTest = im->ReadFile(ASSIMP_TEST_MODELS_DIR "/X/test.x",0); + pTest = im->ReadFile(ASSIMP_TEST_MODELS_DIR "/X/test.x", aiProcess_ValidateDataStructure); } virtual void TearDown() @@ -37,7 +37,7 @@ TEST_F(ExporterTest, testExportToFile) EXPECT_EQ(AI_SUCCESS,ex->Export(pTest,"collada",file)); // check if we can read it again - EXPECT_TRUE(im->ReadFile(file,0)); + EXPECT_TRUE(im->ReadFile(file, aiProcess_ValidateDataStructure)); } // ------------------------------------------------------------------------------------------------ diff --git a/test/unit/utFBXImporterExporter.cpp b/test/unit/utFBXImporterExporter.cpp index f335bc682..2807f801c 100644 --- a/test/unit/utFBXImporterExporter.cpp +++ b/test/unit/utFBXImporterExporter.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -52,7 +53,7 @@ class utFBXImporterExporter : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/FBX/spider.fbx", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/FBX/spider.fbx", aiProcess_ValidateDataStructure ); return nullptr != scene; } }; diff --git a/test/unit/utHMPImportExport.cpp b/test/unit/utHMPImportExport.cpp index 2b71c7211..35bc0bb01 100644 --- a/test/unit/utHMPImportExport.cpp +++ b/test/unit/utHMPImportExport.cpp @@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -50,7 +51,7 @@ class utHMPImportExport : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/HMP/terrain.hmp", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/HMP/terrain.hmp", aiProcess_ValidateDataStructure ); return nullptr != scene; return true; diff --git a/test/unit/utIFCImportExport.cpp b/test/unit/utIFCImportExport.cpp index 79edb8e01..20a11ca57 100644 --- a/test/unit/utIFCImportExport.cpp +++ b/test/unit/utIFCImportExport.cpp @@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -50,7 +51,7 @@ class utIFCImportExport : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/IFC/AC14-FZK-Haus.ifc", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/IFC/AC14-FZK-Haus.ifc", aiProcess_ValidateDataStructure ); return nullptr != scene; return true; diff --git a/test/unit/utIssues.cpp b/test/unit/utIssues.cpp index 2feef922b..a05adc856 100644 --- a/test/unit/utIssues.cpp +++ b/test/unit/utIssues.cpp @@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include "TestModelFactory.h" @@ -66,7 +67,7 @@ TEST_F( utIssues, OpacityBugWhenExporting_727 ) { EXPECT_NE( desc, nullptr ); path.append( desc->fileExtension ); EXPECT_EQ( AI_SUCCESS, exporter.Export( scene, desc->id, path ) ); - const aiScene *newScene( importer.ReadFile( path, 0 ) ); + const aiScene *newScene( importer.ReadFile( path, aiProcess_ValidateDataStructure ) ); EXPECT_TRUE( NULL != newScene ); float newOpacity; if ( newScene->mNumMaterials > 0 ) { diff --git a/test/unit/utObjImportExport.cpp b/test/unit/utObjImportExport.cpp index d9df779ac..dd8adcd80 100644 --- a/test/unit/utObjImportExport.cpp +++ b/test/unit/utObjImportExport.cpp @@ -193,7 +193,7 @@ protected: virtual bool importerTest() { ::Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", aiProcess_ValidateDataStructure ); return nullptr != scene; } @@ -202,7 +202,7 @@ protected: virtual bool exporterTest() { ::Assimp::Importer importer; ::Assimp::Exporter exporter; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", aiProcess_ValidateDataStructure ); EXPECT_NE( nullptr, scene ); EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "obj", ASSIMP_TEST_MODELS_DIR "/OBJ/spider_test.obj" ) ); EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "objnomtl", ASSIMP_TEST_MODELS_DIR "/OBJ/spider_nomtl_test.obj" ) ); @@ -257,7 +257,7 @@ TEST_F( utObjImportExport, issue1111_no_mat_name_Test ) { TEST_F( utObjImportExport, issue809_vertex_color_Test ) { ::Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/cube_with_vertexcolors.obj", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/cube_with_vertexcolors.obj", aiProcess_ValidateDataStructure ); EXPECT_NE( nullptr, scene ); #ifndef ASSIMP_BUILD_NO_EXPORT diff --git a/test/unit/utPMXImporter.cpp b/test/unit/utPMXImporter.cpp index 72916b8ef..3a1ce1f6a 100644 --- a/test/unit/utPMXImporter.cpp +++ b/test/unit/utPMXImporter.cpp @@ -52,7 +52,7 @@ class utPMXImporter : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - /*const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/../models-nonbsd/MMD/Alicia_blade.pmx", 0 ); + /*const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/../models-nonbsd/MMD/Alicia_blade.pmx", aiProcess_ValidateDataStructure ); return nullptr != scene;*/ return true; } diff --git a/test/unit/utQ3DImportExport.cpp b/test/unit/utQ3DImportExport.cpp index d8195309f..e656bc53c 100644 --- a/test/unit/utQ3DImportExport.cpp +++ b/test/unit/utQ3DImportExport.cpp @@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -51,7 +52,7 @@ class utQ3DImportExport : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/Q3D/earth.q3o", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/Q3D/earth.q3o", aiProcess_ValidateDataStructure ); return nullptr != scene; } }; diff --git a/test/unit/utSIBImporter.cpp b/test/unit/utSIBImporter.cpp index ecaa92f26..affa3c9ad 100644 --- a/test/unit/utSIBImporter.cpp +++ b/test/unit/utSIBImporter.cpp @@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "SIBImporter.h" #include +#include #include "AbstractImportExportBase.h" using namespace ::Assimp; @@ -51,7 +52,7 @@ class utSIBImporter : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/SIB/heffalump.sib", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/SIB/heffalump.sib", aiProcess_ValidateDataStructure ); return nullptr != scene; } }; diff --git a/test/unit/utSMDImportExport.cpp b/test/unit/utSMDImportExport.cpp index 9139bb922..610d0c5d4 100644 --- a/test/unit/utSMDImportExport.cpp +++ b/test/unit/utSMDImportExport.cpp @@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "SMDLoader.h" #include +#include #include "AbstractImportExportBase.h" using namespace ::Assimp; @@ -51,7 +52,7 @@ class utSMDImporter : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/SMD/triangle.smd", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/SMD/triangle.smd", aiProcess_ValidateDataStructure ); return nullptr != scene; } }; @@ -73,6 +74,6 @@ TEST_F( utSMDImporter, importTest ) { TEST_F( utSMDImporter, issue_899_Texture_garbage_at_end_of_string_Test ) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/SMD/holy_grailref.smd", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/SMD/holy_grailref.smd", aiProcess_ValidateDataStructure ); EXPECT_NE( nullptr, scene ); } diff --git a/test/unit/utSTLImportExport.cpp b/test/unit/utSTLImportExport.cpp index 0ee3de955..2a3cceaf0 100644 --- a/test/unit/utSTLImportExport.cpp +++ b/test/unit/utSTLImportExport.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -52,7 +53,7 @@ class utSTLImporterExporter : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/STL/Spider_ascii.stl", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/STL/Spider_ascii.stl", aiProcess_ValidateDataStructure ); return nullptr != scene; } }; @@ -63,6 +64,6 @@ TEST_F( utSTLImporterExporter, importXFromFileTest ) { TEST_F( utSTLImporterExporter, test_with_two_solids ) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/STL/triangle_with_two_solids.stl", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/STL/triangle_with_two_solids.stl", aiProcess_ValidateDataStructure ); EXPECT_NE( nullptr, scene ); } diff --git a/test/unit/utX3DImportExport.cpp b/test/unit/utX3DImportExport.cpp index a84c58efc..cb53b3292 100644 --- a/test/unit/utX3DImportExport.cpp +++ b/test/unit/utX3DImportExport.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -52,7 +53,7 @@ class utX3DImportExport : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/X3D/ComputerKeyboard.x3d", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/X3D/ComputerKeyboard.x3d", aiProcess_ValidateDataStructure ); return nullptr != scene; } }; diff --git a/test/unit/utXImporterExporter.cpp b/test/unit/utXImporterExporter.cpp index 042853d3d..4a6300d19 100644 --- a/test/unit/utXImporterExporter.cpp +++ b/test/unit/utXImporterExporter.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -52,7 +53,7 @@ class utXImporterExporter : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/X/test.x", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/X/test.x", aiProcess_ValidateDataStructure ); return nullptr != scene; } }; diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index fa69d648a..c6ccde406 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include using namespace Assimp; @@ -51,7 +52,7 @@ class utglTF2ImportExport : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", 0); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", aiProcess_ValidateDataStructure); return nullptr != scene; } @@ -59,7 +60,7 @@ public: virtual bool exporterTest() { Assimp::Importer importer; Assimp::Exporter exporter; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", aiProcess_ValidateDataStructure ); EXPECT_NE( nullptr, scene ); EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "gltf2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured_out.gltf" ) ); diff --git a/test/unit/utglTFImportExport.cpp b/test/unit/utglTFImportExport.cpp index 18c348299..4e1f765a1 100644 --- a/test/unit/utglTFImportExport.cpp +++ b/test/unit/utglTFImportExport.cpp @@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include using namespace Assimp; @@ -50,7 +51,7 @@ class utglTFImportExport : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF/TwoBoxes/TwoBoxes.gltf", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF/TwoBoxes/TwoBoxes.gltf", aiProcess_ValidateDataStructure ); return nullptr != scene; } }; From b474e75e29696f609f9fade8a8eebb7028304d25 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 21 Nov 2017 18:41:16 +0100 Subject: [PATCH 364/490] 3Mf-Export: add prototypes for relations and rest of 3MF-document. --- code/3DSHelper.h | 16 +++++----- code/D3MFExporter.cpp | 11 +++++-- code/D3MFExporter.h | 4 +++ code/D3MFOpcPackage.cpp | 54 +++++++++++--------------------- code/Exporter.cpp | 29 ++++++++++------- test/unit/utD3MFImportExport.cpp | 16 ++++++++++ 6 files changed, 73 insertions(+), 57 deletions(-) diff --git a/code/3DSHelper.h b/code/3DSHelper.h index d5a51dfb7..9e701c3dd 100644 --- a/code/3DSHelper.h +++ b/code/3DSHelper.h @@ -44,7 +44,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef AI_3DSFILEHELPER_H_INC #define AI_3DSFILEHELPER_H_INC - #include "SpatialSort.h" #include "SmoothingGroups.h" #include "StringUtils.h" @@ -64,16 +63,19 @@ namespace D3DS { /** Discreet3DS class: Helper class for loading 3ds files. Defines chunks * and data structures. */ -class Discreet3DS -{ +class Discreet3DS { private: - inline Discreet3DS() {} + Discreet3DS() { + // empty + } + + ~Discreet3DS() { + // empty + } public: - //! data structure for a single chunk in a .3ds file - struct Chunk - { + struct Chunk { uint16_t Flag; uint32_t Size; } PACK_STRUCT; diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index a602ff7d8..6ffd7c9bb 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -50,7 +50,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "3MFXmlTags.h" namespace Assimp { -namespace D3MF { void ExportScene3MF( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/ ) { std::shared_ptr outfile( pIOSystem->Open( pFile, "wb" ) ); @@ -58,12 +57,16 @@ void ExportScene3MF( const char* pFile, IOSystem* pIOSystem, const aiScene* pSce throw DeadlyExportError( "Could not open output .3ds file: " + std::string( pFile ) ); } - D3MFExporter myExporter( outfile, pScene ); + D3MF::D3MFExporter myExporter( outfile, pScene ); if ( myExporter.validate() ) { bool ok = myExporter.exportAsset(); } } +namespace D3MF { + +#ifndef ASSIMP_BUILD_NO3MF_EXPORTER + D3MFExporter::D3MFExporter( std::shared_ptr outfile, const aiScene* pScene ) : mStream( outfile.get() ) , mScene( pScene ) @@ -101,6 +104,8 @@ bool D3MFExporter::exportAsset() { mOutput << "\n"; + std::string exportedFile = mOutput.str(); + return true; } @@ -179,5 +184,7 @@ void D3MFExporter::writeBuild() { mOutput << "\n"; } +#endif // ASSIMP_BUILD_NO3MF_EXPORTER + } } // Namespace Assimp diff --git a/code/D3MFExporter.h b/code/D3MFExporter.h index 3d3a633c9..1ed2a9bba 100644 --- a/code/D3MFExporter.h +++ b/code/D3MFExporter.h @@ -56,6 +56,8 @@ class IOStream; namespace D3MF { +#ifndef ASSIMP_BUILD_NO3MF_EXPORTER + class D3MFExporter { public: D3MFExporter( std::shared_ptr outfile, const aiScene* pScene ); @@ -78,6 +80,8 @@ private: std::vector mBuildItems; }; +#endif // ASSIMP_BUILD_NO3MF_EXPORTER + } } // Namespace Assimp diff --git a/code/D3MFOpcPackage.cpp b/code/D3MFOpcPackage.cpp index 42f58cb68..dd5c28817 100644 --- a/code/D3MFOpcPackage.cpp +++ b/code/D3MFOpcPackage.cpp @@ -107,7 +107,6 @@ voidpf IOSystem2Unzip::open(voidpf opaque, const char* filename, int mode) { } } - return (voidpf) io_system->Open(filename, mode_fopen); } @@ -177,44 +176,32 @@ zlib_filefunc_def IOSystem2Unzip::get(IOSystem* pIOHandler) { return mapping; } - -class ZipFile : public IOStream -{ +class ZipFile : public IOStream { friend class D3MFZipArchive; public: explicit ZipFile(size_t size); - - ~ZipFile(); - + virtual ~ZipFile(); size_t Read(void* pvBuffer, size_t pSize, size_t pCount ); - size_t Write(const void* /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/); - size_t FileSize() const; - aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/); - size_t Tell() const; - void Flush(); private: - - void* m_Buffer; - + void *m_Buffer; size_t m_Size; - }; ZipFile::ZipFile(size_t size) : m_Size(size) { ai_assert(m_Size != 0); - m_Buffer = malloc(m_Size); + m_Buffer = ::malloc(m_Size); } ZipFile::~ZipFile() { - free(m_Buffer); + ::free(m_Buffer); m_Buffer = NULL; } @@ -227,8 +214,12 @@ size_t ZipFile::Read(void* pvBuffer, size_t pSize, size_t pCount) { return size; } -size_t ZipFile::Write(const void* /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) { - return 0; +size_t ZipFile::Write(const void* pvBuffer, size_t size, size_t pCount ) { + const size_t size_to_write( size * pCount ); + if ( 0 == size_to_write ) { + return 0U; + } + return 0U; } size_t ZipFile::FileSize() const { @@ -247,7 +238,6 @@ void ZipFile::Flush() { // empty } - class D3MFZipArchive : public IOSystem { public: static const unsigned int FileNameSize = 256; @@ -272,14 +262,12 @@ private: // ------------------------------------------------------------------------------------------------ // Constructor. D3MFZipArchive::D3MFZipArchive(IOSystem* pIOHandler, const std::string& rFile) - : m_ZipFileHandle(NULL), m_ArchiveMap() -{ - if (! rFile.empty()) - { +: m_ZipFileHandle(NULL) +, m_ArchiveMap() { + if (! rFile.empty()) { zlib_filefunc_def mapping = IOSystem2Unzip::get(pIOHandler); m_ZipFileHandle = unzOpen2(rFile.c_str(), &mapping); - if(m_ZipFileHandle != NULL) { mapArchive(); } @@ -409,8 +397,7 @@ bool D3MFZipArchive::mapArchive() { // ------------------------------------------------------------------------------------------------ -struct OpcPackageRelationship -{ +struct OpcPackageRelationship { std::string id; std::string type; std::string target; @@ -418,15 +405,10 @@ struct OpcPackageRelationship typedef std::shared_ptr OpcPackageRelationshipPtr; -class OpcPackageRelationshipReader -{ +class OpcPackageRelationshipReader { public: - - OpcPackageRelationshipReader(XmlReader* xmlReader) - { - - while(xmlReader->read()) - { + OpcPackageRelationshipReader(XmlReader* xmlReader) { + while(xmlReader->read()) { if(xmlReader->getNodeType() == irr::io::EXN_ELEMENT && xmlReader->getNodeName() == XmlTag::RELS_RELATIONSHIP_CONTAINER) { diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 76bda9fdf..3fc31a722 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -95,29 +95,30 @@ void ExportSceneGLTF2(const char*, IOSystem*, const aiScene*, const ExportProper void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* ); // ------------------------------------------------------------------------------------------------ // global array of all export formats which Assimp supports in its current build Exporter::ExportFormatEntry gExporters[] = { #ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER - Exporter::ExportFormatEntry( "collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada), + Exporter::ExportFormatEntry( "collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada ), #endif #ifndef ASSIMP_BUILD_NO_X_EXPORTER Exporter::ExportFormatEntry( "x", "X Files", "x", &ExportSceneXFile, - aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs), + aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs ), #endif #ifndef ASSIMP_BUILD_NO_STEP_EXPORTER - Exporter::ExportFormatEntry( "stp", "Step Files", "stp", &ExportSceneStep, 0), + Exporter::ExportFormatEntry( "stp", "Step Files", "stp", &ExportSceneStep, 0 ), #endif #ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER Exporter::ExportFormatEntry( "obj", "Wavefront OBJ format", "obj", &ExportSceneObj, - aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */), + aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ), Exporter::ExportFormatEntry( "objnomtl", "Wavefront OBJ format without material file", "obj", &ExportSceneObjNoMtl, - aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */), + aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ), #endif #ifndef ASSIMP_BUILD_NO_STL_EXPORTER @@ -140,28 +141,32 @@ Exporter::ExportFormatEntry gExporters[] = #ifndef ASSIMP_BUILD_NO_3DS_EXPORTER Exporter::ExportFormatEntry( "3ds", "Autodesk 3DS (legacy)", "3ds" , &ExportScene3DS, - aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices), + aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices ), #endif #ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER Exporter::ExportFormatEntry( "gltf", "GL Transmission Format", "gltf", &ExportSceneGLTF, - aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType), + aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), Exporter::ExportFormatEntry( "glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB, - aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType), + aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf2", &ExportSceneGLTF2, - aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType), + aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), #endif #ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER - Exporter::ExportFormatEntry( "assbin", "Assimp Binary", "assbin" , &ExportSceneAssbin, 0), + Exporter::ExportFormatEntry( "assbin", "Assimp Binary", "assbin" , &ExportSceneAssbin, 0 ), #endif #ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER - Exporter::ExportFormatEntry( "assxml", "Assxml Document", "assxml" , &ExportSceneAssxml, 0), + Exporter::ExportFormatEntry( "assxml", "Assxml Document", "assxml" , &ExportSceneAssxml, 0 ), #endif #ifndef ASSIMP_BUILD_NO_X3D_EXPORTER - Exporter::ExportFormatEntry( "x3d", "Extensible 3D", "x3d" , &ExportSceneX3D, 0), + Exporter::ExportFormatEntry( "x3d", "Extensible 3D", "x3d" , &ExportSceneX3D, 0 ), +#endif + +#ifndef ASSIMP_BUILD_NO3MF_EXPORTER + Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 ) #endif }; diff --git a/test/unit/utD3MFImportExport.cpp b/test/unit/utD3MFImportExport.cpp index 0c322420c..0383a27b0 100644 --- a/test/unit/utD3MFImportExport.cpp +++ b/test/unit/utD3MFImportExport.cpp @@ -43,8 +43,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include #include +#include "D3MFExporter.h" + class utD3MFImporterExporter : public AbstractImportExportBase { public: virtual bool importerTest() { @@ -58,8 +61,21 @@ public: return ( nullptr != scene ); } + + virtual bool exporterTest() { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/3MF/box.3mf", 0 ); + + Assimp::Exporter exporter; + return nullptr != exporter.ExportToBlob( scene, "3mf", 0 ); + } + }; TEST_F(utD3MFImporterExporter, import3MFFromFileTest) { EXPECT_TRUE(importerTest()); } + +TEST_F( utD3MFImporterExporter, export3MFtoMemTest ) { + EXPECT_TRUE( exporterTest() ); +} From 454b8919b0ac5c758c9d6ce52f79269f01ab151f Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 21 Nov 2017 21:34:25 +0100 Subject: [PATCH 365/490] use one header for all xml-tags. --- code/3MFXmlTags.h | 16 ++++++++ code/D3MFExporter.cpp | 22 ++++++++++- code/D3MFExporter.h | 4 +- code/D3MFImporter.cpp | 12 ++---- code/D3MFOpcPackage.cpp | 87 ++++++++++++++--------------------------- code/D3MFOpcPackage.h | 5 +-- 6 files changed, 73 insertions(+), 73 deletions(-) diff --git a/code/3MFXmlTags.h b/code/3MFXmlTags.h index 73d151aa0..9ee17c7e4 100644 --- a/code/3MFXmlTags.h +++ b/code/3MFXmlTags.h @@ -67,6 +67,22 @@ namespace XmlTag { static const std::string item = "item"; static const std::string objectid = "objectid"; static const std::string transform = "transform"; + + static const std::string CONTENT_TYPES_ARCHIVE = "[Content_Types].xml"; + static const std::string ROOT_RELATIONSHIPS_ARCHIVE = "_rels/.rels"; + static const std::string SCHEMA_CONTENTTYPES = "http://schemas.openxmlformats.org/package/2006/content-types"; + static const std::string SCHEMA_RELATIONSHIPS = "http://schemas.openxmlformats.org/package/2006/relationships"; + static const std::string RELS_RELATIONSHIP_CONTAINER = "Relationships"; + static const std::string RELS_RELATIONSHIP_NODE = "Relationship"; + static const std::string RELS_ATTRIB_TARGET = "Target"; + static const std::string RELS_ATTRIB_TYPE = "Type"; + static const std::string RELS_ATTRIB_ID = "Id"; + static const std::string PACKAGE_START_PART_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel"; + static const std::string PACKAGE_PRINT_TICKET_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/printticket"; + static const std::string PACKAGE_TEXTURE_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dtexture"; + static const std::string PACKAGE_CORE_PROPERTIES_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"; + static const std::string PACKAGE_THUMBNAIL_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"; + } } diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index 6ffd7c9bb..6fc3bf065 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -59,7 +59,7 @@ void ExportScene3MF( const char* pFile, IOSystem* pIOSystem, const aiScene* pSce D3MF::D3MFExporter myExporter( outfile, pScene ); if ( myExporter.validate() ) { - bool ok = myExporter.exportAsset(); + bool ok = myExporter.exportArchive(); } } @@ -90,7 +90,25 @@ bool D3MFExporter::validate() { return true; } -bool D3MFExporter::exportAsset() { +bool D3MFExporter::exportArchive() { + bool ok( true ); + ok |= exportRelations(); + ok |= export3DModel(); + + return ok; +} + +bool D3MFExporter::exportRelations() { + mOutput.clear(); + + mOutput << "\n"; + mOutput + return true; +} + +bool D3MFExporter::export3DModel() { + mOutput.clear(); + writeHeader(); mOutput << "<" << XmlTag::model << " " << XmlTag::model_unit << "=\"millimeter\"" << "xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\">" diff --git a/code/D3MFExporter.h b/code/D3MFExporter.h index 1ed2a9bba..0fbd8f100 100644 --- a/code/D3MFExporter.h +++ b/code/D3MFExporter.h @@ -63,7 +63,9 @@ public: D3MFExporter( std::shared_ptr outfile, const aiScene* pScene ); ~D3MFExporter(); bool validate(); - bool exportAsset(); + bool exportArchive(); + bool exportRelations(); + bool export3DModel(); protected: void writeHeader(); diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index 6f77706f9..2fcfefa6d 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -79,15 +79,10 @@ public: scene->mRootNode = new aiNode(); std::vector children; - while(ReadToEndElement(D3MF::XmlTag::model)) - { - - if(xmlReader->getNodeName() == D3MF::XmlTag::object) - { + while(ReadToEndElement(D3MF::XmlTag::model)) { + if(xmlReader->getNodeName() == D3MF::XmlTag::object) { children.push_back(ReadObject(scene)); - } - else if(xmlReader->getNodeName() == D3MF::XmlTag::build) - { + } else if(xmlReader->getNodeName() == D3MF::XmlTag::build) { } } @@ -96,7 +91,6 @@ public: scene->mRootNode->mName.Set( "3MF" ); } - scene->mNumMeshes = static_cast(meshes.size()); scene->mMeshes = new aiMesh*[scene->mNumMeshes](); diff --git a/code/D3MFOpcPackage.cpp b/code/D3MFOpcPackage.cpp index dd5c28817..3d8317f4e 100644 --- a/code/D3MFOpcPackage.cpp +++ b/code/D3MFOpcPackage.cpp @@ -55,30 +55,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include - #include +#include "3MFXmlTags.h" namespace Assimp { namespace D3MF { -namespace XmlTag { - static const std::string CONTENT_TYPES_ARCHIVE = "[Content_Types].xml"; - static const std::string ROOT_RELATIONSHIPS_ARCHIVE = "_rels/.rels"; - static const std::string SCHEMA_CONTENTTYPES = "http://schemas.openxmlformats.org/package/2006/content-types"; - static const std::string SCHEMA_RELATIONSHIPS = "http://schemas.openxmlformats.org/package/2006/relationships"; - static const std::string RELS_RELATIONSHIP_CONTAINER = "Relationships"; - static const std::string RELS_RELATIONSHIP_NODE = "Relationship"; - static const std::string RELS_ATTRIB_TARGET = "Target"; - static const std::string RELS_ATTRIB_TYPE = "Type"; - static const std::string RELS_ATTRIB_ID = "Id"; - static const std::string PACKAGE_START_PART_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel"; - static const std::string PACKAGE_PRINT_TICKET_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/printticket"; - static const std::string PACKAGE_TEXTURE_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dtexture"; - static const std::string PACKAGE_CORE_PROPERTIES_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"; - static const std::string PACKAGE_THUMBNAIL_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"; -} - class IOSystem2Unzip { public: static voidpf open(voidpf opaque, const char* filename, int mode); @@ -194,9 +177,10 @@ private: size_t m_Size; }; -ZipFile::ZipFile(size_t size) : m_Size(size) { +ZipFile::ZipFile(size_t size) +: m_Buffer( nullptr ) +, m_Size(size) { ai_assert(m_Size != 0); - m_Buffer = ::malloc(m_Size); } @@ -431,13 +415,11 @@ public: } } - void ParseAttributes(XmlReader*) - { + void ParseAttributes(XmlReader*) { } - void ParseChildNode(XmlReader* xmlReader) - { + void ParseChildNode(XmlReader* xmlReader) { OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship()); relPtr->id = xmlReader->getAttributeValue(XmlTag::RELS_ATTRIB_ID.c_str()); @@ -446,42 +428,41 @@ public: m_relationShips.push_back(relPtr); } + std::vector m_relationShips; }; // ------------------------------------------------------------------------------------------------ D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile) - : m_RootStream(nullptr) -{ - zipArchive.reset(new D3MF::D3MFZipArchive( pIOHandler, rFile )); - if(!zipArchive->isOpen()) { +: mRootStream(nullptr) +, mZipArchive() { + mZipArchive.reset( new D3MF::D3MFZipArchive( pIOHandler, rFile ) ); + if(!mZipArchive->isOpen()) { throw DeadlyImportError("Failed to open file " + rFile+ "."); } std::vector fileList; - zipArchive->getFileList(fileList); + mZipArchive->getFileList(fileList); - for(auto& file: fileList){ + for (auto& file: fileList) { if(file == D3MF::XmlTag::ROOT_RELATIONSHIPS_ARCHIVE) { //PkgRelationshipReader pkgRelReader(file, archive); - ai_assert(zipArchive->Exists(file.c_str())); + ai_assert(mZipArchive->Exists(file.c_str())); - IOStream *fileStream = zipArchive->Open(file.c_str()); + IOStream *fileStream = mZipArchive->Open(file.c_str()); ai_assert(fileStream != nullptr); std::string rootFile = ReadPackageRootRelationship(fileStream); - if(rootFile.size() > 0 && rootFile[0] == '/') - rootFile = rootFile.substr(1); + if ( rootFile.size() > 0 && rootFile[ 0 ] == '/' ) { + rootFile = rootFile.substr( 1 ); + } DefaultLogger::get()->debug(rootFile); - m_RootStream = zipArchive->Open(rootFile.c_str()); - - ai_assert(m_RootStream != nullptr); - - + mRootStream = mZipArchive->Open(rootFile.c_str()); + ai_assert(mRootStream != nullptr); // const size_t size = zipArchive->FileSize(); // m_Data.resize( size ); @@ -492,50 +473,40 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile) // m_Data.clear(); // return false; // } - zipArchive->Close( fileStream ); + mZipArchive->Close( fileStream ); - } - else if( file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) - { + } else if( file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) { } } } -D3MFOpcPackage::~D3MFOpcPackage() -{ - +D3MFOpcPackage::~D3MFOpcPackage() { + // empty } -IOStream* D3MFOpcPackage::RootStream() const -{ - return m_RootStream; +IOStream* D3MFOpcPackage::RootStream() const { + return mRootStream; } - -std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream* stream) -{ - +std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream* stream) { std::unique_ptr xmlStream(new CIrrXML_IOStreamReader(stream)); std::unique_ptr xml(irr::io::createIrrXMLReader(xmlStream.get())); OpcPackageRelationshipReader reader(xml.get()); - auto itr = std::find_if(reader.m_relationShips.begin(), reader.m_relationShips.end(), [](const OpcPackageRelationshipPtr& rel){ return rel->type == XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE; }); - - if(itr == reader.m_relationShips.end()) throw DeadlyImportError("Cannot find" + XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE); return (*itr)->target; } -} //namespace D3MF +} // Namespace D3MF -} +} // Namespace Assimp #endif //ASSIMP_BUILD_NO_3MF_IMPORTER diff --git a/code/D3MFOpcPackage.h b/code/D3MFOpcPackage.h index 9ac9ade58..40ab2e462 100644 --- a/code/D3MFOpcPackage.h +++ b/code/D3MFOpcPackage.h @@ -48,7 +48,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "irrXMLWrapper.h" namespace Assimp { - namespace D3MF { typedef irr::io::IrrXMLReader XmlReader; @@ -66,8 +65,8 @@ protected: std::string ReadPackageRootRelationship(IOStream* stream); private: - IOStream* m_RootStream; - std::unique_ptr zipArchive; + IOStream* mRootStream; + std::unique_ptr mZipArchive; }; } From fdb52723c465f37f9ebdc7b9be6c1835df51fb2b Mon Sep 17 00:00:00 2001 From: Giuseppe Barbieri Date: Wed, 22 Nov 2017 16:11:46 +0100 Subject: [PATCH 366/490] Update Readme.md --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index a7187246b..1f6c4118f 100644 --- a/Readme.md +++ b/Readme.md @@ -120,7 +120,7 @@ Take a look into the `INSTALL` file. Our build system is CMake, if you used CMak * [Pascal](port/AssimpPascal/Readme.md) * [Javascript (Alpha)](https://github.com/makc/assimp2json) * [Unity 3d Plugin](https://www.assetstore.unity3d.com/en/#!/content/91777) -* [JVM](https://github.com/kotlin-graphics/assimp) Full jvm port (currently supported obj, ply, stl, ~collada) +* [JVM](https://github.com/kotlin-graphics/assimp) Full jvm port (currently supported obj, ply, stl, collada, md2) ### Other tools ### [open3mod](https://github.com/acgessler/open3mod) is a powerful 3D model viewer based on Assimp's import and export abilities. From 09a5946dbd9a1a6b667e2ced842d3d56c06895e6 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 23 Nov 2017 22:47:18 +0100 Subject: [PATCH 367/490] Prepare archive structure. --- code/D3MFExporter.cpp | 42 ++++++++++++++--- code/D3MFExporter.h | 9 +++- code/D3MFOpcPackage.cpp | 2 +- code/FIReader.cpp | 6 +-- code/MMDImporter.cpp | 2 +- include/assimp/IOSystem.hpp | 89 +++++++++++++++++++++++++++++-------- 6 files changed, 119 insertions(+), 31 deletions(-) diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index 6fc3bf065..4d3d45f8c 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -57,9 +57,9 @@ void ExportScene3MF( const char* pFile, IOSystem* pIOSystem, const aiScene* pSce throw DeadlyExportError( "Could not open output .3ds file: " + std::string( pFile ) ); } - D3MF::D3MFExporter myExporter( outfile, pScene ); + D3MF::D3MFExporter myExporter( outfile, pIOSystem, pScene ); if ( myExporter.validate() ) { - bool ok = myExporter.exportArchive(); + bool ok = myExporter.exportArchive(pFile); } } @@ -67,8 +67,9 @@ namespace D3MF { #ifndef ASSIMP_BUILD_NO3MF_EXPORTER -D3MFExporter::D3MFExporter( std::shared_ptr outfile, const aiScene* pScene ) -: mStream( outfile.get() ) +D3MFExporter::D3MFExporter( std::shared_ptr outfile, IOSystem* pIOSystem, const aiScene* pScene ) +: mIOSystem( pIOSystem ) +, mStream( outfile.get() ) , mScene( pScene ) , mBuildItems() { // empty @@ -78,6 +79,26 @@ D3MFExporter::~D3MFExporter() { // empty } +bool D3MFExporter::createFileStructure( const char *file ) { + if ( !mIOSystem->CreateDirectory( file ) ) { + return false; + } + + if ( !mIOSystem->ChangeDirectory( file ) ) { + return false; + } + + if ( !mIOSystem->CreateDirectory( "3D" ) ) { + return false; + } + + if ( !mIOSystem->CreateDirectory( "_rels" ) ) { + return false; + } + + return true; +} + bool D3MFExporter::validate() { if ( nullptr == mStream ) { return false; @@ -90,8 +111,9 @@ bool D3MFExporter::validate() { return true; } -bool D3MFExporter::exportArchive() { +bool D3MFExporter::exportArchive( const char *file ) { bool ok( true ); + ok |= createFileStructure( file ); ok |= exportRelations(); ok |= export3DModel(); @@ -102,7 +124,10 @@ bool D3MFExporter::exportRelations() { mOutput.clear(); mOutput << "\n"; - mOutput + //mOutput + + writeRelInfoToFile(); + return true; } @@ -117,6 +142,8 @@ bool D3MFExporter::export3DModel() { writeObjects(); + writeModelToArchive(); + mOutput << "\n"; writeBuild(); @@ -202,6 +229,9 @@ void D3MFExporter::writeBuild() { mOutput << "\n"; } +bool writeModelToArchive(); +bool writeRelInfoToFile(); + #endif // ASSIMP_BUILD_NO3MF_EXPORTER } diff --git a/code/D3MFExporter.h b/code/D3MFExporter.h index 0fbd8f100..eacf91069 100644 --- a/code/D3MFExporter.h +++ b/code/D3MFExporter.h @@ -53,6 +53,7 @@ struct aiMesh; namespace Assimp { class IOStream; +class IOSystem; namespace D3MF { @@ -60,10 +61,11 @@ namespace D3MF { class D3MFExporter { public: - D3MFExporter( std::shared_ptr outfile, const aiScene* pScene ); + D3MFExporter( std::shared_ptr outfile, IOSystem* pIOSystem, const aiScene* pScene ); ~D3MFExporter(); bool validate(); - bool exportArchive(); + bool createFileStructure( const char *file ); + bool exportArchive( const char *file ); bool exportRelations(); bool export3DModel(); @@ -74,8 +76,11 @@ protected: void writeVertex( const aiVector3D &pos ); void writeFaces( aiMesh *mesh ); void writeBuild(); + bool writeModelToArchive(); + bool writeRelInfoToFile(); private: + IOSystem *mIOSystem; IOStream *mStream; const aiScene *mScene; std::ostringstream mOutput; diff --git a/code/D3MFOpcPackage.cpp b/code/D3MFOpcPackage.cpp index 3d8317f4e..ed792461b 100644 --- a/code/D3MFOpcPackage.cpp +++ b/code/D3MFOpcPackage.cpp @@ -416,7 +416,7 @@ public: } void ParseAttributes(XmlReader*) { - + // empty } void ParseChildNode(XmlReader* xmlReader) { diff --git a/code/FIReader.cpp b/code/FIReader.cpp index 53cff257a..f8058e784 100755 --- a/code/FIReader.cpp +++ b/code/FIReader.cpp @@ -714,7 +714,7 @@ public: if (floatValue) { return floatValue->value.size() == 1 ? floatValue->value.front() : 0; } - return atof(attr->value->toString().c_str()); + return static_cast( atof( attr->value->toString().c_str() ) ); } virtual float getAttributeValueAsFloat(int idx) const /*override*/ { @@ -725,7 +725,7 @@ public: if (floatValue) { return floatValue->value.size() == 1 ? floatValue->value.front() : 0; } - return atof(attributes[idx].value->toString().c_str()); + return static_cast( atof( attributes[ idx ].value->toString().c_str() ) ); } virtual const char* getNodeName() const /*override*/ { @@ -1792,7 +1792,7 @@ public: virtual void registerDecoder(const std::string &/*algorithmUri*/, std::unique_ptr /*decoder*/) /*override*/ {} - virtual void registerVocabulary(const std::string &/*vocabularyUri*/, const FIVocabulary */*vocabulary*/) /*override*/ {} + virtual void registerVocabulary(const std::string &/*vocabularyUri*/, const FIVocabulary * /*vocabulary*/) /*override*/ {} private: diff --git a/code/MMDImporter.cpp b/code/MMDImporter.cpp index cf7c87c8a..01f009519 100644 --- a/code/MMDImporter.cpp +++ b/code/MMDImporter.cpp @@ -107,7 +107,7 @@ const aiImporterDesc *MMDImporter::GetInfo() const { return &desc; } // ------------------------------------------------------------------------------------------------ // MMD import implementation void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene, - IOSystem */*pIOHandler*/) { + IOSystem * /*pIOHandler*/) { // Read file by istream std::filebuf fb; if (!fb.open(file, std::ios::in | std::ios::binary)) { diff --git a/include/assimp/IOSystem.hpp b/include/assimp/IOSystem.hpp index e7a216e4e..35f58e8b6 100644 --- a/include/assimp/IOSystem.hpp +++ b/include/assimp/IOSystem.hpp @@ -56,10 +56,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "types.h" +#ifdef _WIN32 +# include +# include +# include +#else +# include +# include +#endif // _WIN32 + #include namespace Assimp { -class IOStream; + + class IOStream; // --------------------------------------------------------------------------- /** @brief CPP-API: Interface to the file system. @@ -198,20 +208,35 @@ public: */ virtual bool PopDirectory(); + // ------------------------------------------------------------------- + /** @brief CReates an new directory at the given path. + * @param path [in] The path to create. + * @return True, when a directory was created. False if the directory + * cannot be created. + */ + virtual bool CreateDirectory( const std::string &path ); + + // ------------------------------------------------------------------- + /** @brief Will change the current directory to the given path. + * @param path [in] The path to change to. + * @return True, when the directory has changed successfully. + */ + virtual bool ChangeDirectory( const std::string &path ); + private: std::vector m_pathStack; }; // ---------------------------------------------------------------------------- -AI_FORCE_INLINE IOSystem::IOSystem() : - m_pathStack() -{ +AI_FORCE_INLINE +IOSystem::IOSystem() +: m_pathStack() { // empty } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE IOSystem::~IOSystem() -{ +AI_FORCE_INLINE +IOSystem::~IOSystem() { // empty } @@ -222,9 +247,8 @@ AI_FORCE_INLINE IOSystem::~IOSystem() // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- -AI_FORCE_INLINE IOStream* IOSystem::Open(const std::string& pFile, - const std::string& pMode) -{ +AI_FORCE_INLINE +IOStream* IOSystem::Open(const std::string& pFile, const std::string& pMode) { // NOTE: // For compatibility, interface was changed to const char* to // avoid crashes between binary incompatible STL versions @@ -232,8 +256,8 @@ AI_FORCE_INLINE IOStream* IOSystem::Open(const std::string& pFile, } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE bool IOSystem::Exists( const std::string& pFile) const -{ +AI_FORCE_INLINE +bool IOSystem::Exists( const std::string& pFile) const { // NOTE: // For compatibility, interface was changed to const char* to // avoid crashes between binary incompatible STL versions @@ -241,9 +265,8 @@ AI_FORCE_INLINE bool IOSystem::Exists( const std::string& pFile) const } // ---------------------------------------------------------------------------- -inline bool IOSystem::ComparePaths (const std::string& one, - const std::string& second) const -{ +AI_FORCE_INLINE +bool IOSystem::ComparePaths (const std::string& one, const std::string& second) const { // NOTE: // For compatibility, interface was changed to const char* to // avoid crashes between binary incompatible STL versions @@ -251,7 +274,8 @@ inline bool IOSystem::ComparePaths (const std::string& one, } // ---------------------------------------------------------------------------- -inline bool IOSystem::PushDirectory( const std::string &path ) { +AI_FORCE_INLINE +bool IOSystem::PushDirectory( const std::string &path ) { if ( path.empty() ) { return false; } @@ -262,7 +286,8 @@ inline bool IOSystem::PushDirectory( const std::string &path ) { } // ---------------------------------------------------------------------------- -inline const std::string &IOSystem::CurrentDirectory() const { +AI_FORCE_INLINE +const std::string &IOSystem::CurrentDirectory() const { if ( m_pathStack.empty() ) { static const std::string Dummy(""); return Dummy; @@ -271,12 +296,14 @@ inline const std::string &IOSystem::CurrentDirectory() const { } // ---------------------------------------------------------------------------- -inline size_t IOSystem::StackSize() const { +AI_FORCE_INLINE +size_t IOSystem::StackSize() const { return m_pathStack.size(); } // ---------------------------------------------------------------------------- -inline bool IOSystem::PopDirectory() { +AI_FORCE_INLINE +bool IOSystem::PopDirectory() { if ( m_pathStack.empty() ) { return false; } @@ -287,6 +314,32 @@ inline bool IOSystem::PopDirectory() { } // ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::CreateDirectory( const std::string &path ) { + if ( path.empty() ) { + return false; + } + +#ifdef _WIN32 + return 0 != ::_mkdir( path.c_str() ); +#else + return 0 != ::mkdir( path.c_str(), 0777 ); +#endif // _WIN32 +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::ChangeDirectory( const std::string &path ) { + if ( path.empty() ) { + return false; + } + +#ifdef _WIN32 + return 0 != ::_chdir( path.c_str() ); +#else + return 0 != ::chdir( path.c_str() ); +#endif // _WIN32 +} } //!ns Assimp From 0bdb375804be69126522bf8e52763755a052b770 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 24 Nov 2017 18:59:37 +0100 Subject: [PATCH 368/490] Add missing file export into archive. --- code/3DSExporter.cpp | 1 - code/3MFXmlTags.h | 4 +- code/D3MFExporter.cpp | 94 ++++++++++++++++++++++++++++++++--------- code/D3MFExporter.h | 14 +++--- code/D3MFOpcPackage.cpp | 6 --- code/D3MFOpcPackage.h | 6 +++ 6 files changed, 92 insertions(+), 33 deletions(-) diff --git a/code/3DSExporter.cpp b/code/3DSExporter.cpp index 91c241ef7..2f561fcd6 100644 --- a/code/3DSExporter.cpp +++ b/code/3DSExporter.cpp @@ -39,7 +39,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ - #ifndef ASSIMP_BUILD_NO_EXPORT #ifndef ASSIMP_BUILD_NO_3DS_EXPORTER diff --git a/code/3MFXmlTags.h b/code/3MFXmlTags.h index 9ee17c7e4..7e47422f1 100644 --- a/code/3MFXmlTags.h +++ b/code/3MFXmlTags.h @@ -85,5 +85,5 @@ namespace XmlTag { } -} -} +} // Namespace D3MF +} // Namespace Assimp diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index 4d3d45f8c..189985b90 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -39,25 +39,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ #include "D3MFExporter.h" + #include #include #include #include - #include #include "Exceptional.h" #include "3MFXmlTags.h" +#include "D3MFOpcPackage.h" namespace Assimp { void ExportScene3MF( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/ ) { - std::shared_ptr outfile( pIOSystem->Open( pFile, "wb" ) ); - if ( !outfile ) { - throw DeadlyExportError( "Could not open output .3ds file: " + std::string( pFile ) ); - } - - D3MF::D3MFExporter myExporter( outfile, pIOSystem, pScene ); + D3MF::D3MFExporter myExporter( pFile, pIOSystem, pScene ); if ( myExporter.validate() ) { bool ok = myExporter.exportArchive(pFile); } @@ -67,16 +63,20 @@ namespace D3MF { #ifndef ASSIMP_BUILD_NO3MF_EXPORTER -D3MFExporter::D3MFExporter( std::shared_ptr outfile, IOSystem* pIOSystem, const aiScene* pScene ) +D3MFExporter::D3MFExporter( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene ) : mIOSystem( pIOSystem ) -, mStream( outfile.get() ) +, mArchiveName( pFile ) , mScene( pScene ) -, mBuildItems() { +, mBuildItems() +, mRelations() { // empty } D3MFExporter::~D3MFExporter() { - // empty + for ( size_t i = 0; i < mRelations.size(); ++i ) { + delete mRelations[ i ]; + } + mRelations.clear(); } bool D3MFExporter::createFileStructure( const char *file ) { @@ -100,7 +100,7 @@ bool D3MFExporter::createFileStructure( const char *file ) { } bool D3MFExporter::validate() { - if ( nullptr == mStream ) { + if ( mArchiveName.empty() ) { return false; } @@ -117,6 +117,9 @@ bool D3MFExporter::exportArchive( const char *file ) { ok |= exportRelations(); ok |= export3DModel(); + if ( ok ) { + createZipArchiveFromeFileStructure(); + } return ok; } @@ -124,9 +127,16 @@ bool D3MFExporter::exportRelations() { mOutput.clear(); mOutput << "\n"; - //mOutput + mOutput << "\n"; - writeRelInfoToFile(); + for ( size_t i = 0; i < mRelations.size(); ++i ) { + mOutput << "target << " "; + mOutput << "id=\"" << mRelations[i]->id << " "; + mOutput << "Type=\"" << mRelations[ i ]->type << "/>\n"; + } + mOutput << "\n"; + + writeRelInfoToFile( "_rels", ".rels" ); return true; } @@ -142,14 +152,21 @@ bool D3MFExporter::export3DModel() { writeObjects(); - writeModelToArchive(); mOutput << "\n"; writeBuild(); mOutput << "\n"; - std::string exportedFile = mOutput.str(); + OpcPackageRelationship *info = new OpcPackageRelationship; + info->id = mArchiveName; + info->target = "rel0"; + info->type = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel"; + mRelations.push_back( info ); + + writeModelToArchive( "3D", mArchiveName ); + + mOutput.clear(); return true; } @@ -229,10 +246,49 @@ void D3MFExporter::writeBuild() { mOutput << "\n"; } -bool writeModelToArchive(); -bool writeRelInfoToFile(); +void D3MFExporter::writeModelToArchive( const std::string &folder, const std::string &modelName ) { + const std::string &oldFolder( mIOSystem->CurrentDirectory() ); + if ( folder != oldFolder ) { + mIOSystem->PushDirectory( oldFolder ); + mIOSystem->ChangeDirectory( folder ); + + const std::string &exportTxt( mOutput.str() ); + std::shared_ptr outfile( mIOSystem->Open( modelName, "wb" ) ); + if ( !outfile ) { + throw DeadlyExportError( "Could not open output model file: " + std::string( modelName ) ); + } + + const size_t writtenBytes( outfile->Write( exportTxt.c_str(), sizeof( char ), exportTxt.size() ) ); + + mIOSystem->ChangeDirectory( ".." ); + mIOSystem->PopDirectory(); + } +} + +void D3MFExporter::writeRelInfoToFile( const std::string &folder, const std::string &relName ) { + const std::string &oldFolder( mIOSystem->CurrentDirectory() ); + if ( folder != oldFolder ) { + mIOSystem->PushDirectory( oldFolder ); + mIOSystem->ChangeDirectory( folder ); + + const std::string &exportTxt( mOutput.str() ); + std::shared_ptr outfile( mIOSystem->Open( relName, "wb" ) ); + if ( !outfile ) { + throw DeadlyExportError( "Could not open output model file: " + std::string( relName ) ); + } + + const size_t writtenBytes( outfile->Write( exportTxt.c_str(), sizeof( char ), exportTxt.size() ) ); + + mIOSystem->ChangeDirectory( ".." ); + mIOSystem->PopDirectory(); + } +} + +void D3MFExporter::createZipArchiveFromeFileStructure() { + +} #endif // ASSIMP_BUILD_NO3MF_EXPORTER -} +} // Namespace D3MF } // Namespace Assimp diff --git a/code/D3MFExporter.h b/code/D3MFExporter.h index eacf91069..2fd647a4c 100644 --- a/code/D3MFExporter.h +++ b/code/D3MFExporter.h @@ -59,9 +59,11 @@ namespace D3MF { #ifndef ASSIMP_BUILD_NO3MF_EXPORTER +struct OpcPackageRelationship; + class D3MFExporter { public: - D3MFExporter( std::shared_ptr outfile, IOSystem* pIOSystem, const aiScene* pScene ); + D3MFExporter( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene ); ~D3MFExporter(); bool validate(); bool createFileStructure( const char *file ); @@ -76,19 +78,21 @@ protected: void writeVertex( const aiVector3D &pos ); void writeFaces( aiMesh *mesh ); void writeBuild(); - bool writeModelToArchive(); - bool writeRelInfoToFile(); + void writeModelToArchive( const std::string &folder, const std::string &modelName ); + void writeRelInfoToFile( const std::string &folder, const std::string &relName ); + void createZipArchiveFromeFileStructure(); private: IOSystem *mIOSystem; - IOStream *mStream; + std::string mArchiveName; const aiScene *mScene; std::ostringstream mOutput; std::vector mBuildItems; + std::vector mRelations; }; #endif // ASSIMP_BUILD_NO3MF_EXPORTER -} +} // Namespace D3MF } // Namespace Assimp diff --git a/code/D3MFOpcPackage.cpp b/code/D3MFOpcPackage.cpp index ed792461b..1db8cab13 100644 --- a/code/D3MFOpcPackage.cpp +++ b/code/D3MFOpcPackage.cpp @@ -381,12 +381,6 @@ bool D3MFZipArchive::mapArchive() { // ------------------------------------------------------------------------------------------------ -struct OpcPackageRelationship { - std::string id; - std::string type; - std::string target; -}; - typedef std::shared_ptr OpcPackageRelationshipPtr; class OpcPackageRelationshipReader { diff --git a/code/D3MFOpcPackage.h b/code/D3MFOpcPackage.h index 40ab2e462..818415e9f 100644 --- a/code/D3MFOpcPackage.h +++ b/code/D3MFOpcPackage.h @@ -53,6 +53,12 @@ namespace D3MF { typedef irr::io::IrrXMLReader XmlReader; typedef std::shared_ptr XmlReaderPtr; +struct OpcPackageRelationship { + std::string id; + std::string type; + std::string target; +}; + class D3MFZipArchive; class D3MFOpcPackage { From 6c59c83e0f628f99d340d61e405e6272d3f0907b Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 24 Nov 2017 19:48:21 +0100 Subject: [PATCH 369/490] add missing include --- include/assimp/IOSystem.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/assimp/IOSystem.hpp b/include/assimp/IOSystem.hpp index 35f58e8b6..4a88c4a31 100644 --- a/include/assimp/IOSystem.hpp +++ b/include/assimp/IOSystem.hpp @@ -63,6 +63,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #else # include # include +# include #endif // _WIN32 #include From d8d5cf1a2d0c14c0889446b4830e98de5697bc62 Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Fri, 24 Nov 2017 19:50:25 +0100 Subject: [PATCH 370/490] Copy assimp dll to unit folder on windows --- test/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1c5d593de..741da9534 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -177,6 +177,13 @@ ELSE( WIN32 ) SET( platform_libs pthread ) ENDIF( WIN32 ) +IF( WIN32 ) + ADD_CUSTOM_COMMAND(TARGET unit + PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ + MAIN_DEPENDENCY assimp) +ENDIF( WIN32 ) + IF(MSVC) add_definitions(-D_CRT_SECURE_NO_WARNINGS) ENDIF(MSVC) From c63263b02579600921f1c5455af5e4f0a0887fbf Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Fri, 24 Nov 2017 20:16:30 +0100 Subject: [PATCH 371/490] Fix typo on gltf2 camera parameters --- code/glTF2Asset.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index ca18e87e0..7534c2230 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -934,7 +934,7 @@ inline void Camera::Read(Value& obj, Asset& /*r*/) { type = MemberOrDefault(obj, "type", Camera::Perspective); - const char* subobjId = (type == Camera::Orthographic) ? "ortographic" : "perspective"; + const char* subobjId = (type == Camera::Orthographic) ? "orthographic" : "perspective"; Value* it = FindObject(obj, subobjId); if (!it) throw DeadlyImportError("GLTF: Camera missing its parameters"); From f80e8b39a18f1fc24b46a7693fc73c2f0553a228 Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Fri, 24 Nov 2017 22:08:50 +0100 Subject: [PATCH 372/490] Fix warning C4138: '*/' found outside of comment on MSVC14 --- code/D3MFImporter.cpp | 2 +- code/FIReader.cpp | 4 ++-- code/MMDImporter.cpp | 2 +- code/MMDPmxParser.cpp | 2 +- code/OpenGEXExporter.cpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index 6d61a0669..e52c72346 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -344,7 +344,7 @@ bool D3MFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool return false; } -void D3MFImporter::SetupProperties(const Importer */*pImp*/) +void D3MFImporter::SetupProperties(const Importer * /*pImp*/) { } diff --git a/code/FIReader.cpp b/code/FIReader.cpp index 53cff257a..a0b9b39e1 100755 --- a/code/FIReader.cpp +++ b/code/FIReader.cpp @@ -1790,9 +1790,9 @@ public: return nullptr; } - virtual void registerDecoder(const std::string &/*algorithmUri*/, std::unique_ptr /*decoder*/) /*override*/ {} + virtual void registerDecoder(const std::string & /*algorithmUri*/, std::unique_ptr /*decoder*/) /*override*/ {} - virtual void registerVocabulary(const std::string &/*vocabularyUri*/, const FIVocabulary */*vocabulary*/) /*override*/ {} + virtual void registerVocabulary(const std::string & /*vocabularyUri*/, const FIVocabulary * /*vocabulary*/) /*override*/ {} private: diff --git a/code/MMDImporter.cpp b/code/MMDImporter.cpp index cf7c87c8a..01f009519 100644 --- a/code/MMDImporter.cpp +++ b/code/MMDImporter.cpp @@ -107,7 +107,7 @@ const aiImporterDesc *MMDImporter::GetInfo() const { return &desc; } // ------------------------------------------------------------------------------------------------ // MMD import implementation void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene, - IOSystem */*pIOHandler*/) { + IOSystem * /*pIOHandler*/) { // Read file by istream std::filebuf fb; if (!fb.open(file, std::ios::in | std::ios::binary)) { diff --git a/code/MMDPmxParser.cpp b/code/MMDPmxParser.cpp index 5be370f26..970cbc31e 100644 --- a/code/MMDPmxParser.cpp +++ b/code/MMDPmxParser.cpp @@ -471,7 +471,7 @@ namespace pmx stream->read((char*) &this->is_near, sizeof(uint8_t)); } - void PmxSoftBody::Read(std::istream */*stream*/, PmxSetting */*setting*/) + void PmxSoftBody::Read(std::istream * /*stream*/, PmxSetting * /*setting*/) { // 未実装 std::cerr << "Not Implemented Exception" << std::endl; diff --git a/code/OpenGEXExporter.cpp b/code/OpenGEXExporter.cpp index b3597656b..0f2b1c35b 100644 --- a/code/OpenGEXExporter.cpp +++ b/code/OpenGEXExporter.cpp @@ -51,7 +51,7 @@ OpenGEXExporter::OpenGEXExporter() { OpenGEXExporter::~OpenGEXExporter() { } -bool OpenGEXExporter::exportScene( const char */*filename*/, const aiScene* /*pScene*/ ) { +bool OpenGEXExporter::exportScene( const char * /*filename*/, const aiScene* /*pScene*/ ) { return true; } From 273f6b0267a04aabbde3e3b92e4ae48bb01e2aae Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Fri, 24 Nov 2017 22:13:38 +0100 Subject: [PATCH 373/490] Fix MSVC14 warning cast double to real --- code/FindDegenerates.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/FindDegenerates.cpp b/code/FindDegenerates.cpp index 5a4a132ef..f9c04133a 100644 --- a/code/FindDegenerates.cpp +++ b/code/FindDegenerates.cpp @@ -93,7 +93,7 @@ void FindDegeneratesProcess::Execute( aiScene* pScene) { static ai_real heron( ai_real a, ai_real b, ai_real c ) { ai_real s = (a + b + c) / 2; - ai_real area = pow((s * ( s - a ) * ( s - b ) * ( s - c ) ), 0.5 ); + ai_real area = pow((s * ( s - a ) * ( s - b ) * ( s - c ) ), (ai_real)0.5 ); return area; } @@ -102,7 +102,7 @@ static ai_real distance3D( const aiVector3D &vA, aiVector3D &vB ) { const ai_real ly = ( vB.y - vA.y ); const ai_real lz = ( vB.z - vA.z ); ai_real a = lx*lx + ly*ly + lz*lz; - ai_real d = pow( a, 0.5 ); + ai_real d = pow( a, (ai_real)0.5 ); return d; } From d529dd17f920a8d5fb33547f866c26b668a265ec Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Fri, 24 Nov 2017 22:29:58 +0100 Subject: [PATCH 374/490] Fix warning cast double to float --- code/FIReader.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/code/FIReader.cpp b/code/FIReader.cpp index a0b9b39e1..7aaa32fd2 100755 --- a/code/FIReader.cpp +++ b/code/FIReader.cpp @@ -60,6 +60,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "MemoryIOWrapper.h" #include "irrXMLWrapper.h" #include "../contrib/utf8cpp/source/utf8.h" +#include "fast_atof.h" #include #include #include @@ -714,7 +715,7 @@ public: if (floatValue) { return floatValue->value.size() == 1 ? floatValue->value.front() : 0; } - return atof(attr->value->toString().c_str()); + return fast_atof(attr->value->toString().c_str()); } virtual float getAttributeValueAsFloat(int idx) const /*override*/ { @@ -725,7 +726,7 @@ public: if (floatValue) { return floatValue->value.size() == 1 ? floatValue->value.front() : 0; } - return atof(attributes[idx].value->toString().c_str()); + return fast_atof(attributes[idx].value->toString().c_str()); } virtual const char* getNodeName() const /*override*/ { From eb452b28a286f706c8936292884c3a35972659e9 Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Sat, 25 Nov 2017 11:38:12 +0100 Subject: [PATCH 375/490] Fix warning on MSVC14 --- code/ColladaExporter.cpp | 2 +- code/FindInvalidDataProcess.cpp | 2 +- code/STLLoader.cpp | 4 ++-- code/glTF2Asset.inl | 2 +- code/glTF2Exporter.cpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/code/ColladaExporter.cpp b/code/ColladaExporter.cpp index 03a955b8f..4a10d5845 100644 --- a/code/ColladaExporter.cpp +++ b/code/ColladaExporter.cpp @@ -1309,7 +1309,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) // Combine the above transformations aiMatrix4x4 mat = TranslationM * RotationM * ScalingM; - for( size_t j = 0; j < 4; ++j) { + for( unsigned int j = 0; j < 4; ++j) { keyframes.insert(keyframes.end(), mat[j], mat[j] + 4); } } diff --git a/code/FindInvalidDataProcess.cpp b/code/FindInvalidDataProcess.cpp index 0113e4c5b..c37d8956c 100644 --- a/code/FindInvalidDataProcess.cpp +++ b/code/FindInvalidDataProcess.cpp @@ -339,7 +339,7 @@ void FindInvalidDataProcess::ProcessAnimationChannel (aiNodeAnim* anim) int FindInvalidDataProcess::ProcessMesh (aiMesh* pMesh) { bool ret = false; - std::vector dirtyMask(pMesh->mNumVertices, pMesh->mNumFaces); + std::vector dirtyMask(pMesh->mNumVertices, pMesh->mNumFaces != 0); // Ignore elements that are not referenced by vertices. // (they are, for example, caused by the FindDegenerates step) diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index 41f8aad03..ab2dcf36e 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -248,7 +248,7 @@ void STLImporter::LoadASCIIFile( aiNode *root ) { std::vector meshIndices; aiMesh* pMesh = new aiMesh(); pMesh->mMaterialIndex = 0; - meshIndices.push_back( meshes.size() ); + meshIndices.push_back((unsigned int) meshes.size() ); meshes.push_back(pMesh); aiNode *node = new aiNode; node->mParent = root; @@ -383,7 +383,7 @@ void STLImporter::LoadASCIIFile( aiNode *root ) { pScene->mMeshes[ i ] = meshes[i]; } - root->mNumChildren = nodes.size(); + root->mNumChildren = (unsigned int) nodes.size(); root->mChildren = new aiNode*[ root->mNumChildren ]; for ( size_t i=0; imChildren[ i ] = nodes[ i ]; diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index ca18e87e0..246cc1c93 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -212,7 +212,7 @@ unsigned int LazyDict::Remove(const char* id) mObjs.erase(mObjs.begin() + index); //update index of object in mObjs; - for (size_t i = index; i < mObjs.size(); ++i) { + for (unsigned int i = index; i < mObjs.size(); ++i) { T *obj = mObjs[i]; obj->index = i; diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index e6d4d1a3b..c1a803c1f 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -445,7 +445,7 @@ void glTF2Exporter::ExportMaterials() mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS ) { // convert specular color to luminance - float specularIntensity = specularColor[0] * 0.2125 + specularColor[1] * 0.7154 + specularColor[2] * 0.0721; + float specularIntensity = specularColor[0] * 0.2125f + specularColor[1] * 0.7154f + specularColor[2] * 0.0721f; //normalize shininess (assuming max is 1000) with an inverse exponentional curve float normalizedShininess = std::sqrt(shininess / 1000); From e53d4735b0ca5fd879168a135129e42887a74511 Mon Sep 17 00:00:00 2001 From: awefers Date: Mon, 27 Nov 2017 11:30:18 -0500 Subject: [PATCH 376/490] Fix transform matrices multiplication order per glTF2.0 spec Closes #1568 --- code/glTF2Importer.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 297f2bc72..7e7483484 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -515,7 +515,13 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector& CopyValue(node.translation.value, trans); aiMatrix4x4 t; aiMatrix4x4::Translation(trans, t); - matrix = t * matrix; + matrix = matrix * t; + } + + if (node.rotation.isPresent) { + aiQuaternion rot; + CopyValue(node.rotation.value, rot); + matrix = matrix * aiMatrix4x4(rot.GetMatrix()); } if (node.scale.isPresent) { @@ -523,14 +529,7 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector& CopyValue(node.scale.value, scal); aiMatrix4x4 s; aiMatrix4x4::Scaling(scal, s); - matrix = s * matrix; - } - - - if (node.rotation.isPresent) { - aiQuaternion rot; - CopyValue(node.rotation.value, rot); - matrix = aiMatrix4x4(rot.GetMatrix()) * matrix; + matrix = matrix * s; } } From 0b04ae1d91a561aa2bf6c40edf8f490b45b7b7fd Mon Sep 17 00:00:00 2001 From: awefers Date: Mon, 27 Nov 2017 11:56:58 -0500 Subject: [PATCH 377/490] Preserve node names when importing glTF2.0 Closes #1522 --- code/glTF2Importer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 297f2bc72..ffc51ef0e 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -492,7 +492,7 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector& { Node& node = *ptr; - aiNode* ainode = new aiNode(node.id); + aiNode* ainode = new aiNode(node.name); if (!node.children.empty()) { ainode->mNumChildren = unsigned(node.children.size()); From 0031165789e7dc65ae07d0e4071aede498aaf46d Mon Sep 17 00:00:00 2001 From: awefers Date: Mon, 27 Nov 2017 13:45:15 -0500 Subject: [PATCH 378/490] Add support for tangents in glTF2.0 import Closes #1562 --- code/glTF2Asset.h | 2 +- code/glTF2Asset.inl | 5 ++++- code/glTF2Importer.cpp | 26 +++++++++++++++++++++++++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 765f5e0f8..cece307d9 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -775,7 +775,7 @@ namespace glTF2 PrimitiveMode mode; struct Attributes { - AccessorList position, normal, texcoord, color, joint, jointmatrix, weight; + AccessorList position, normal, tangent, texcoord, color, joint, jointmatrix, weight; } attributes; Ref indices; diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 29ab9a49a..549df747e 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -867,6 +867,9 @@ namespace { else if ((pos = Compare(attr, "NORMAL"))) { v = &(p.attributes.normal); } + else if ((pos = Compare(attr, "TANGENT"))) { + v = &(p.attributes.tangent); + } else if ((pos = Compare(attr, "TEXCOORD"))) { v = &(p.attributes.texcoord); } @@ -906,7 +909,7 @@ inline void Mesh::Read(Value& pJSON_Object, Asset& pAsset_Root) for (Value::MemberIterator it = attrs->MemberBegin(); it != attrs->MemberEnd(); ++it) { if (!it->value.IsUint()) continue; const char* attr = it->name.GetString(); - // Valid attribute semantics include POSITION, NORMAL, TEXCOORD, COLOR, JOINT, JOINTMATRIX, + // Valid attribute semantics include POSITION, NORMAL, TANGENT, TEXCOORD, COLOR, JOINT, JOINTMATRIX, // and WEIGHT.Attribute semantics can be of the form[semantic]_[set_index], e.g., TEXCOORD_0, TEXCOORD_1, etc. int undPos = 0; diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index 297f2bc72..7b05dc000 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -362,7 +362,31 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r) attr.position[0]->ExtractData(aim->mVertices); } - if (attr.normal.size() > 0 && attr.normal[0]) attr.normal[0]->ExtractData(aim->mNormals); + if (attr.normal.size() > 0 && attr.normal[0]) { + attr.normal[0]->ExtractData(aim->mNormals); + + // only extract tangents if normals are present + if (attr.tangent.size() > 0 && attr.tangent[0]) { + // generate bitangents from normals and tangents according to spec + struct Tangent + { + aiVector3D xyz; + ai_real w; + } *tangents = nullptr; + + attr.tangent[0]->ExtractData(tangents); + + aim->mTangents = new aiVector3D[aim->mNumVertices]; + aim->mBitangents = new aiVector3D[aim->mNumVertices]; + + for (unsigned int i = 0; i < aim->mNumVertices; ++i) { + aim->mTangents[i] = tangents[i].xyz; + aim->mBitangents[i] = (aim->mNormals[i] ^ tangents[i].xyz) * tangents[i].w; + } + + delete tangents; + } + } for (size_t tc = 0; tc < attr.texcoord.size() && tc < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++tc) { attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc]); From 3dfca3bc84c8e25916d8744a50e77605df334a57 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 27 Nov 2017 21:48:33 +0100 Subject: [PATCH 379/490] 3MF: add export to a given archive. --- code/CMakeLists.txt | 9 + code/D3MFExporter.cpp | 79 +- code/D3MFExporter.h | 6 +- contrib/zip/.gitignore | 38 + contrib/zip/.travis.yml | 10 + contrib/zip/CMakeLists.txt | 18 + contrib/zip/README.md | 139 + contrib/zip/UNLICENSE | 26 + contrib/zip/appveyor.yml | 14 + contrib/zip/src/miniz.h | 4928 +++++++++++++++++++++++++++++++ contrib/zip/src/zip.c | 640 ++++ contrib/zip/src/zip.h | 193 ++ contrib/zip/test/CMakeLists.txt | 7 + contrib/zip/test/test.c | 105 + contrib/zip/zip.png | Bin 0 -> 5275 bytes 15 files changed, 6152 insertions(+), 60 deletions(-) create mode 100644 contrib/zip/.gitignore create mode 100644 contrib/zip/.travis.yml create mode 100644 contrib/zip/CMakeLists.txt create mode 100644 contrib/zip/README.md create mode 100644 contrib/zip/UNLICENSE create mode 100644 contrib/zip/appveyor.yml create mode 100644 contrib/zip/src/miniz.h create mode 100644 contrib/zip/src/zip.c create mode 100644 contrib/zip/src/zip.h create mode 100644 contrib/zip/test/CMakeLists.txt create mode 100644 contrib/zip/test/test.c create mode 100644 contrib/zip/zip.png diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 9b3b96618..9ce1120d4 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -743,6 +743,14 @@ SET( unzip_SRCS ) SOURCE_GROUP( unzip FILES ${unzip_SRCS}) +SET( ziplib_SRCS + ../contrib/zip/src/miniz.h + ../contrib/zip/src/zip.c + ../contrib/zip/src/zip.h +) + +SOURCE_GROUP( ziplib FILES ${ziplib_SRCS} ) + SET ( openddl_parser_SRCS ../contrib/openddlparser/code/OpenDDLParser.cpp ../contrib/openddlparser/code/DDLNode.cpp @@ -854,6 +862,7 @@ SET( assimp_src ${Clipper_SRCS} ${openddl_parser_SRCS} ${open3dgc_SRCS} + ${ziplib_SRCS} # Necessary to show the headers in the project when using the VC++ generator: ${PUBLIC_HEADERS} diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index 189985b90..524f54400 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -50,12 +50,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "3MFXmlTags.h" #include "D3MFOpcPackage.h" +#include + namespace Assimp { void ExportScene3MF( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/ ) { D3MF::D3MFExporter myExporter( pFile, pIOSystem, pScene ); if ( myExporter.validate() ) { bool ok = myExporter.exportArchive(pFile); + if ( !ok ) { + throw DeadlyExportError( "Could not export 3MP archive: " + std::string( pFile ) ); + } } } @@ -66,6 +71,7 @@ namespace D3MF { D3MFExporter::D3MFExporter( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene ) : mIOSystem( pIOSystem ) , mArchiveName( pFile ) +, m_zipArchive( nullptr ) , mScene( pScene ) , mBuildItems() , mRelations() { @@ -79,26 +85,6 @@ D3MFExporter::~D3MFExporter() { mRelations.clear(); } -bool D3MFExporter::createFileStructure( const char *file ) { - if ( !mIOSystem->CreateDirectory( file ) ) { - return false; - } - - if ( !mIOSystem->ChangeDirectory( file ) ) { - return false; - } - - if ( !mIOSystem->CreateDirectory( "3D" ) ) { - return false; - } - - if ( !mIOSystem->CreateDirectory( "_rels" ) ) { - return false; - } - - return true; -} - bool D3MFExporter::validate() { if ( mArchiveName.empty() ) { return false; @@ -113,13 +99,14 @@ bool D3MFExporter::validate() { bool D3MFExporter::exportArchive( const char *file ) { bool ok( true ); - ok |= createFileStructure( file ); + + m_zipArchive = zip_open( file, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w' ); + if ( nullptr == m_zipArchive ) { + return false; + } ok |= exportRelations(); ok |= export3DModel(); - if ( ok ) { - createZipArchiveFromeFileStructure(); - } return ok; } @@ -247,45 +234,21 @@ void D3MFExporter::writeBuild() { } void D3MFExporter::writeModelToArchive( const std::string &folder, const std::string &modelName ) { - const std::string &oldFolder( mIOSystem->CurrentDirectory() ); - if ( folder != oldFolder ) { - mIOSystem->PushDirectory( oldFolder ); - mIOSystem->ChangeDirectory( folder ); + const std::string entry = folder + "/" + mArchiveName; + zip_entry_open( m_zipArchive, entry.c_str() ); - const std::string &exportTxt( mOutput.str() ); - std::shared_ptr outfile( mIOSystem->Open( modelName, "wb" ) ); - if ( !outfile ) { - throw DeadlyExportError( "Could not open output model file: " + std::string( modelName ) ); - } + const std::string &exportTxt( mOutput.str() ); + zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() ); - const size_t writtenBytes( outfile->Write( exportTxt.c_str(), sizeof( char ), exportTxt.size() ) ); - - mIOSystem->ChangeDirectory( ".." ); - mIOSystem->PopDirectory(); - } + zip_entry_close( m_zipArchive ); } void D3MFExporter::writeRelInfoToFile( const std::string &folder, const std::string &relName ) { - const std::string &oldFolder( mIOSystem->CurrentDirectory() ); - if ( folder != oldFolder ) { - mIOSystem->PushDirectory( oldFolder ); - mIOSystem->ChangeDirectory( folder ); - - const std::string &exportTxt( mOutput.str() ); - std::shared_ptr outfile( mIOSystem->Open( relName, "wb" ) ); - if ( !outfile ) { - throw DeadlyExportError( "Could not open output model file: " + std::string( relName ) ); - } - - const size_t writtenBytes( outfile->Write( exportTxt.c_str(), sizeof( char ), exportTxt.size() ) ); - - mIOSystem->ChangeDirectory( ".." ); - mIOSystem->PopDirectory(); - } -} - -void D3MFExporter::createZipArchiveFromeFileStructure() { - + const std::string entry = folder + "/" + "_rels"; + zip_entry_open( m_zipArchive, entry.c_str() ); + const std::string &exportTxt( mOutput.str() ); + zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() ); + zip_entry_close( m_zipArchive ); } #endif // ASSIMP_BUILD_NO3MF_EXPORTER diff --git a/code/D3MFExporter.h b/code/D3MFExporter.h index 2fd647a4c..9a80844ef 100644 --- a/code/D3MFExporter.h +++ b/code/D3MFExporter.h @@ -50,6 +50,8 @@ struct aiNode; struct aiMaterial; struct aiMesh; +struct zip_t; + namespace Assimp { class IOStream; @@ -66,7 +68,6 @@ public: D3MFExporter( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene ); ~D3MFExporter(); bool validate(); - bool createFileStructure( const char *file ); bool exportArchive( const char *file ); bool exportRelations(); bool export3DModel(); @@ -80,11 +81,12 @@ protected: void writeBuild(); void writeModelToArchive( const std::string &folder, const std::string &modelName ); void writeRelInfoToFile( const std::string &folder, const std::string &relName ); - void createZipArchiveFromeFileStructure(); + void createZipArchiveFromeFileStructure( const char* pFile ); private: IOSystem *mIOSystem; std::string mArchiveName; + zip_t *m_zipArchive; const aiScene *mScene; std::ostringstream mOutput; std::vector mBuildItems; diff --git a/contrib/zip/.gitignore b/contrib/zip/.gitignore new file mode 100644 index 000000000..4f9972c56 --- /dev/null +++ b/contrib/zip/.gitignore @@ -0,0 +1,38 @@ +/build/ +/test/build/ +/xcodeproj/ + +# Object files +*.o +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib +*.suo + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Temporary +*.swp +.DS_Store diff --git a/contrib/zip/.travis.yml b/contrib/zip/.travis.yml new file mode 100644 index 000000000..d8ccb728d --- /dev/null +++ b/contrib/zip/.travis.yml @@ -0,0 +1,10 @@ +language: c +# Compiler selection +compiler: + - clang + - gcc +# Build steps +script: + - mkdir build + - cd build + - cmake -DCMAKE_BUILD_TYPE=Debug .. && make && make test diff --git a/contrib/zip/CMakeLists.txt b/contrib/zip/CMakeLists.txt new file mode 100644 index 000000000..450ef3a98 --- /dev/null +++ b/contrib/zip/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 2.8) +project(zip) + +if (MSVC) + # Use secure functions by defaualt and suppress warnings about "deprecated" functions + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1") +endif (MSVC) + +# zip +set(SRC src/miniz.h src/zip.h src/zip.c) +add_library(${CMAKE_PROJECT_NAME} ${SRC}) + +# test +enable_testing() +add_subdirectory(test) + diff --git a/contrib/zip/README.md b/contrib/zip/README.md new file mode 100644 index 000000000..24de5e61a --- /dev/null +++ b/contrib/zip/README.md @@ -0,0 +1,139 @@ +### A portable (OSX/Linux/Windows), simple zip library written in C +This is done by hacking awesome [miniz](https://code.google.com/p/miniz) library and layering functions on top of the miniz v1.15 API. + +[![Windows][win-badge]][win-link] [![OS X][osx-linux-badge]][osx-linux-link] + +[win-badge]: https://img.shields.io/appveyor/ci/kuba--/zip/master.svg?label=windows "AppVeyor build status" +[win-link]: https://ci.appveyor.com/project/kuba--/zip "AppVeyor build status" +[osx-linux-badge]: https://img.shields.io/travis/kuba--/zip/master.svg?label=linux/osx "Travis CI build status" +[osx-linux-link]: https://travis-ci.org/kuba--/zip "Travis CI build status" + +# The Idea + +... Some day, I was looking for zip library written in C for my project, but I could not find anything simple enough and lightweight. +Everything what I tried required 'crazy mental gymnastics' to integrate or had some limitations or was too heavy. +I hate frameworks, factories and adding new dependencies. If I must to install all those dependencies and link new library, I'm getting almost sick. +I wanted something powerfull and small enough, so I could add just a few files and compile them into my project. +And finally I found miniz. +Miniz is a lossless, high performance data compression library in a single source file. I only needed simple interface to append buffers or files to the current zip-entry. Thanks to this feature I'm able to merge many files/buffers and compress them on-the-fly. + +It was the reason, why I decided to write zip module on top of the miniz. It required a little bit hacking and wrapping some functions, but I kept simplicity. So, you can grab these 3 files and compile them into your project. I hope that interface is also extremely simple, so you will not have any problems to understand it. + +# Examples + +* Create a new zip archive with default compression level. +```c + struct zip_t *zip = zip_open("foo.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 'w'); + { + zip_entry_open(zip, "foo-1.txt"); + { + char *buf = "Some data here..."; + zip_entry_write(zip, buf, strlen(buf)); + } + zip_entry_close(zip); + + zip_entry_open(zip, "foo-2.txt"); + { + // merge 3 files into one entry and compress them on-the-fly. + zip_entry_fwrite(zip, "foo-2.1.txt"); + zip_entry_fwrite(zip, "foo-2.2.txt"); + zip_entry_fwrite(zip, "foo-2.3.txt"); + } + zip_entry_close(zip); + } + zip_close(zip); +``` + +* Append to the existing zip archive. +```c + struct zip_t *zip = zip_open("foo.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 'a'); + { + zip_entry_open(zip, "foo-3.txt"); + { + char *buf = "Append some data here..."; + zip_entry_write(zip, buf, strlen(buf)); + } + zip_entry_close(zip); + } + zip_close(zip); +``` + +* Extract a zip archive into a folder. +```c + int on_extract_entry(const char *filename, void *arg) { + static int i = 0; + int n = *(int *)arg; + printf("Extracted: %s (%d of %d)\n", filename, ++i, n); + + return 0; + } + + int arg = 2; + zip_extract("foo.zip", "/tmp", on_extract_entry, &arg); +``` + +* Extract a zip entry into memory. +```c + void *buf = NULL; + size_t bufsize; + + struct zip_t *zip = zip_open("foo.zip", 0, 'r'); + { + zip_entry_open(zip, "foo-1.txt"); + { + zip_entry_read(zip, &buf, &bufsize); + } + zip_entry_close(zip); + } + zip_close(zip); + + free(buf); +``` + +* Extract a zip entry into memory using callback. +```c + struct buffer_t { + char *data; + size_t size; + }; + + static size_t on_extract(void *arg, unsigned long long offset, const void *data, size_t size) { + struct buffer_t *buf = (struct buffer_t *)arg; + buf->data = realloc(buf->data, buf->size + size + 1); + assert(NULL != buf->data); + + memcpy(&(buf->data[buf->size]), data, size); + buf->size += size; + buf->data[buf->size] = 0; + + return size; + } + + struct buffer_t buf = {0}; + struct zip_t *zip = zip_open("foo.zip", 0, 'r'); + { + zip_entry_open(zip, "foo-1.txt"); + { + zip_entry_extract(zip, on_extract, &buf); + } + zip_entry_close(zip); + } + zip_close(zip); + + free(buf.data); +``` + + +* Extract a zip entry into a file. +```c + struct zip_t *zip = zip_open("foo.zip", 0, 'r'); + { + zip_entry_open(zip, "foo-2.txt"); + { + zip_entry_fread(zip, "foo-2.txt"); + } + zip_entry_close(zip); + } + zip_close(zip); +``` + diff --git a/contrib/zip/UNLICENSE b/contrib/zip/UNLICENSE new file mode 100644 index 000000000..ed7cccdf2 --- /dev/null +++ b/contrib/zip/UNLICENSE @@ -0,0 +1,26 @@ +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +*/ diff --git a/contrib/zip/appveyor.yml b/contrib/zip/appveyor.yml new file mode 100644 index 000000000..297cad8b0 --- /dev/null +++ b/contrib/zip/appveyor.yml @@ -0,0 +1,14 @@ +version: 1.0.{build} +build_script: +- cmd: >- + cd c:\projects\zip + + mkdir build + + cd build + + cmake -G"Visual Studio 14" -DCMAKE_BUILD_TYPE=Debug .. + + cmake --build . --config %CMAKE_BUILD_TYPE% + + ctest --verbose -C "Debug" diff --git a/contrib/zip/src/miniz.h b/contrib/zip/src/miniz.h new file mode 100644 index 000000000..da86f6e8f --- /dev/null +++ b/contrib/zip/src/miniz.h @@ -0,0 +1,4928 @@ +/* + miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing + See "unlicense" statement at the end of this file. + Rich Geldreich , last updated Oct. 13, 2013 + Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt + + Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define + MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). + + * Change History + 10/13/13 v1.15 r4 - Interim bugfix release while I work on the next major release with Zip64 support (almost there!): + - Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug + would only have occured in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place() + (which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag). + - Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size + - Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries. + Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice). + - Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes + - mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed + - Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6. + - Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti + - Merged MZ_FORCEINLINE fix from hdeanclark + - Fix include before config #ifdef, thanks emil.brink + - Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can + set it to 1 for real-time compression). + - Merged in some compiler fixes from paulharris's github repro. + - Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3. + - Added example6.c, which dumps an image of the mandelbrot set to a PNG file. + - Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more. + - In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled +  - In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch + 5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include (thanks fermtect). + 5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. + - Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. + - Eliminated a bunch of warnings when compiling with GCC 32-bit/64. + - Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly + "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). + - Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. + - Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. + - Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. + - Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) + - Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). + 4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's. + level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson for the feedback/bug report. + 5/28/11 v1.11 - Added statement from unlicense.org + 5/27/11 v1.10 - Substantial compressor optimizations: + - Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a + - Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). + - Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. + - Refactored the compression code for better readability and maintainability. + - Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large + drop in throughput on some files). + 5/15/11 v1.09 - Initial stable release. + + * Low-level Deflate/Inflate implementation notes: + + Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or + greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses + approximately as well as zlib. + + Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function + coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory + block large enough to hold the entire file. + + The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. + + * zlib-style API notes: + + miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in + zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. + Supports raw deflate streams or standard zlib streams with adler-32 checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. + I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but + there are no guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by + Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to + get the job done with minimal fuss. There are simple API's to retrieve file information, read files from + existing archives, create new archives, append new files to existing archives, or clone archive data from + one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), + or you can specify custom file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central + directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one example) can be used to identify + multiple versions of the same file in an archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and + retrieve detailed info on each file by calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data + to disk and builds an exact image of the central directory in memory. The central directory image is written + all at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file data to any power of 2 alignment, + which can be useful when the archive will be read from optical media. Also, the writer supports placing + arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still + readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is to call this function: + + mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, + const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + + The archive will be created if it doesn't already exist, otherwise it'll be appended to. + Note the appending is done in-place and is not an atomic operation, so if something goes wrong + during the operation it's possible the archive could be left without a central directory (although the local + file headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to + preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and + you're done. This is safe but requires a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), + append new files as needed, then finalize the archive which will write an updated central directory to the + original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a + possibility that the archive's central directory could be lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the + below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your target platform: + #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 + #define MINIZ_LITTLE_ENDIAN 1 + #define MINIZ_HAS_64BIT_REGISTERS 1 + + * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz + uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files + (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). +*/ + +#ifndef MINIZ_HEADER_INCLUDED +#define MINIZ_HEADER_INCLUDED + +#include + +// Defines to completely disable specific portions of miniz.c: +// If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. + +// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. +//#define MINIZ_NO_STDIO + +// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or +// get/set file times, and the C run-time funcs that get/set times won't be called. +// The current downside is the times written to your archives will be from 1979. +//#define MINIZ_NO_TIME + +// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. +//#define MINIZ_NO_ARCHIVE_APIS + +// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's. +//#define MINIZ_NO_ARCHIVE_WRITING_APIS + +// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. +//#define MINIZ_NO_ZLIB_APIS + +// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. +//#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES + +// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. +// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc +// callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user +// functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. +//#define MINIZ_NO_MALLOC + +#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) + // TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux + #define MINIZ_NO_TIME +#endif + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) + #include +#endif + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +// MINIZ_X86_OR_X64_CPU is only used to help set the below macros. +#define MINIZ_X86_OR_X64_CPU 1 +#endif + +#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. +#define MINIZ_LITTLE_ENDIAN 1 +#endif + +#if MINIZ_X86_OR_X64_CPU +// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +// Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). +#define MINIZ_HAS_64BIT_REGISTERS 1 +#endif + +#ifdef __APPLE__ +#define ftello64 ftello +#define fseeko64 fseeko +#define fopen64 fopen +#define freopen64 freopen +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// ------------------- zlib-style API Definitions. + +// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! +typedef unsigned long mz_ulong; + +// mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. +void mz_free(void *p); + +#define MZ_ADLER32_INIT (1) +// mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +// mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. +mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +// Compression strategies. +enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; + +// Method +#define MZ_DEFLATED 8 + +#ifndef MINIZ_NO_ZLIB_APIS + +// Heap allocation callbacks. +// Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); + +#define MZ_VERSION "9.1.15" +#define MZ_VERNUM 0x91F0 +#define MZ_VER_MAJOR 9 +#define MZ_VER_MINOR 1 +#define MZ_VER_REVISION 15 +#define MZ_VER_SUBREVISION 0 + +// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). +enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; + +// Return status codes. MZ_PARAM_ERROR is non-standard. +enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 }; + +// Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. +enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 }; + +// Window bits +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +// Compression/decompression stream struct. +typedef struct mz_stream_s +{ + const unsigned char *next_in; // pointer to next byte to read + unsigned int avail_in; // number of bytes available at next_in + mz_ulong total_in; // total number of bytes consumed so far + + unsigned char *next_out; // pointer to next byte to write + unsigned int avail_out; // number of bytes that can be written to next_out + mz_ulong total_out; // total number of bytes produced so far + + char *msg; // error msg (unused) + struct mz_internal_state *state; // internal state, allocated by zalloc/zfree + + mz_alloc_func zalloc; // optional heap allocation function (defaults to malloc) + mz_free_func zfree; // optional heap free function (defaults to free) + void *opaque; // heap alloc function user pointer + + int data_type; // data_type (unused) + mz_ulong adler; // adler32 of the source or uncompressed data + mz_ulong reserved; // not used +} mz_stream; + +typedef mz_stream *mz_streamp; + +// Returns the version string of miniz.c. +const char *mz_version(void); + +// mz_deflateInit() initializes a compressor with default options: +// Parameters: +// pStream must point to an initialized mz_stream struct. +// level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. +// level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. +// (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) +// Return values: +// MZ_OK on success. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_PARAM_ERROR if the input parameters are bogus. +// MZ_MEM_ERROR on out of memory. +int mz_deflateInit(mz_streamp pStream, int level); + +// mz_deflateInit2() is like mz_deflate(), except with more control: +// Additional parameters: +// method must be MZ_DEFLATED +// window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) +// mem_level must be between [1, 9] (it's checked but ignored by miniz.c) +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +// Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). +int mz_deflateReset(mz_streamp pStream); + +// mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. +// Parameters: +// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. +// flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. +// Return values: +// MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). +// MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_PARAM_ERROR if one of the parameters is invalid. +// MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) +int mz_deflate(mz_streamp pStream, int flush); + +// mz_deflateEnd() deinitializes a compressor: +// Return values: +// MZ_OK on success. +// MZ_STREAM_ERROR if the stream is bogus. +int mz_deflateEnd(mz_streamp pStream); + +// mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +// Single-call compression functions mz_compress() and mz_compress2(): +// Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +// mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). +mz_ulong mz_compressBound(mz_ulong source_len); + +// Initializes a decompressor. +int mz_inflateInit(mz_streamp pStream); + +// mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: +// window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). +int mz_inflateInit2(mz_streamp pStream, int window_bits); + +// Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. +// Parameters: +// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. +// flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. +// On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). +// MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. +// Return values: +// MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. +// MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_DATA_ERROR if the deflate stream is invalid. +// MZ_PARAM_ERROR if one of the parameters is invalid. +// MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again +// with more input data, or with more room in the output buffer (except when using single call decompression, described above). +int mz_inflate(mz_streamp pStream, int flush); + +// Deinitializes a decompressor. +int mz_inflateEnd(mz_streamp pStream); + +// Single-call decompression. +// Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); + +// Returns a string description of the specified error code, or NULL if the error code is invalid. +const char *mz_error(int err); + +// Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. +// Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES + typedef unsigned char Byte; + typedef unsigned int uInt; + typedef mz_ulong uLong; + typedef Byte Bytef; + typedef uInt uIntf; + typedef char charf; + typedef int intf; + typedef void *voidpf; + typedef uLong uLongf; + typedef void *voidp; + typedef void *const voidpc; + #define Z_NULL 0 + #define Z_NO_FLUSH MZ_NO_FLUSH + #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH + #define Z_SYNC_FLUSH MZ_SYNC_FLUSH + #define Z_FULL_FLUSH MZ_FULL_FLUSH + #define Z_FINISH MZ_FINISH + #define Z_BLOCK MZ_BLOCK + #define Z_OK MZ_OK + #define Z_STREAM_END MZ_STREAM_END + #define Z_NEED_DICT MZ_NEED_DICT + #define Z_ERRNO MZ_ERRNO + #define Z_STREAM_ERROR MZ_STREAM_ERROR + #define Z_DATA_ERROR MZ_DATA_ERROR + #define Z_MEM_ERROR MZ_MEM_ERROR + #define Z_BUF_ERROR MZ_BUF_ERROR + #define Z_VERSION_ERROR MZ_VERSION_ERROR + #define Z_PARAM_ERROR MZ_PARAM_ERROR + #define Z_NO_COMPRESSION MZ_NO_COMPRESSION + #define Z_BEST_SPEED MZ_BEST_SPEED + #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION + #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION + #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY + #define Z_FILTERED MZ_FILTERED + #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY + #define Z_RLE MZ_RLE + #define Z_FIXED MZ_FIXED + #define Z_DEFLATED MZ_DEFLATED + #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS + #define alloc_func mz_alloc_func + #define free_func mz_free_func + #define internal_state mz_internal_state + #define z_stream mz_stream + #define deflateInit mz_deflateInit + #define deflateInit2 mz_deflateInit2 + #define deflateReset mz_deflateReset + #define deflate mz_deflate + #define deflateEnd mz_deflateEnd + #define deflateBound mz_deflateBound + #define compress mz_compress + #define compress2 mz_compress2 + #define compressBound mz_compressBound + #define inflateInit mz_inflateInit + #define inflateInit2 mz_inflateInit2 + #define inflate mz_inflate + #define inflateEnd mz_inflateEnd + #define uncompress mz_uncompress + #define crc32 mz_crc32 + #define adler32 mz_adler32 + #define MAX_WBITS 15 + #define MAX_MEM_LEVEL 9 + #define zError mz_error + #define ZLIB_VERSION MZ_VERSION + #define ZLIB_VERNUM MZ_VERNUM + #define ZLIB_VER_MAJOR MZ_VER_MAJOR + #define ZLIB_VER_MINOR MZ_VER_MINOR + #define ZLIB_VER_REVISION MZ_VER_REVISION + #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION + #define zlibVersion mz_version + #define zlib_version mz_version() +#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES + +#endif // MINIZ_NO_ZLIB_APIS + +// ------------------- Types and macros + +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef long long mz_int64; +typedef unsigned long long mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +// An attempt to work around MSVC's spammy "warning C4127: conditional expression is constant" message. +#ifdef _MSC_VER + #define MZ_MACRO_END while (0, 0) +#else + #define MZ_MACRO_END while (0) +#endif + +// ------------------- ZIP archive reading/writing + +#ifndef MINIZ_NO_ARCHIVE_APIS + +enum +{ + MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256 +}; + +typedef struct +{ + mz_uint32 m_file_index; + mz_uint32 m_central_dir_ofs; + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; +#ifndef MINIZ_NO_TIME + time_t m_time; +#endif + mz_uint32 m_crc32; + mz_uint64 m_comp_size; + mz_uint64 m_uncomp_size; + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + mz_uint64 m_local_header_ofs; + mz_uint32 m_comment_size; + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum +{ + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef struct mz_zip_archive_tag +{ + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + mz_uint m_total_files; + mz_zip_mode m_zip_mode; + + mz_uint m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef enum +{ + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800 +} mz_zip_flags; + +// ZIP archive reading + +// Inits a ZIP archive reader. +// These functions read and validate the archive's central directory. +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags); +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +#endif + +// Returns the total number of files in the archive. +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +// Returns detailed information about an archive file entry. +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +// Determines if an archive file entry is a directory entry. +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +// Retrieves the filename of an archive file entry. +// Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +// Attempts to locates a file in the archive's central directory. +// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH +// Returns -1 if the file cannot be found. +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + +// Extracts a archive file to a memory buffer using no memory allocation. +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +// Extracts a archive file to a memory buffer. +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +// Extracts a archive file to a dynamically allocated heap buffer. +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +// Extracts a archive file using a callback function to output the file's data. +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +// Extracts a archive file to a disk file and sets its last accessed and modified times. +// This function only extracts files, not archive directory records. +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); +#endif + +// Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. +mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +// ZIP archive writing + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +// Inits a ZIP archive writer. +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +#endif + +// Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. +// For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. +// For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). +// Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. +// Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before +// the archive is finalized the file's central directory will be hosed. +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); + +// Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. +// To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer. +// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); + +#ifndef MINIZ_NO_STDIO +// Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. +// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +#endif + +// Adds a file to an archive by fully cloning the data from another archive. +// This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields. +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index); + +// Finalizes the archive by writing the central directory records followed by the end of central directory record. +// After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). +// An archive must be manually finalized by calling this function for it to be valid. +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize); + +// Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. +// Note for the archive to be valid, it must have been finalized before ending. +mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +// Misc. high-level helper functions: + +// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. +// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + +// Reads a single file from an archive into a heap block. +// Returns NULL on failure. +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); + +#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +#endif // #ifndef MINIZ_NO_ARCHIVE_APIS + +// ------------------- Low-level Decompression API Definitions + +// Decompression flags used by tinfl_decompress(). +// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. +// TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. +// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). +// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. +enum +{ + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +// High level decompression functions: +// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). +// On entry: +// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. +// On return: +// Function returns a pointer to the decompressed data, or NULL on failure. +// *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. +// The caller must call mz_free() on the returned block when it's no longer needed. +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. +// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +// tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. +// Returns 1 on success or 0 on failure. +typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; + +// Max size of LZ dictionary. +#define TINFL_LZ_DICT_SIZE 32768 + +// Return status. +typedef enum +{ + TINFL_STATUS_BAD_PARAM = -3, + TINFL_STATUS_ADLER32_MISMATCH = -2, + TINFL_STATUS_FAILED = -1, + TINFL_STATUS_DONE = 0, + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +// Initializes the decompressor to its initial state. +#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +// Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. +// This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); + +// Internal/private bits follow. +enum +{ + TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +typedef struct +{ + mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; +} tinfl_huff_table; + +#if MINIZ_HAS_64BIT_REGISTERS + #define TINFL_USE_64BIT_BITBUF 1 +#endif + +#if TINFL_USE_64BIT_BITBUF + typedef mz_uint64 tinfl_bit_buf_t; + #define TINFL_BITBUF_SIZE (64) +#else + typedef mz_uint32 tinfl_bit_buf_t; + #define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag +{ + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +// ------------------- Low-level Compression API Definitions + +// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). +#define TDEFL_LESS_MEMORY 0 + +// tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): +// TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). +enum +{ + TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF +}; + +// TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. +// TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). +// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. +// TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). +// TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) +// TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. +// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. +// TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. +// The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +// High level compression functions: +// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). +// On entry: +// pSrc_buf, src_buf_len: Pointer and size of source block to compress. +// flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. +// On return: +// Function returns a pointer to the compressed data, or NULL on failure. +// *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. +// The caller must free() the returned block when it's no longer needed. +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +// tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. +// Returns 0 on failure. +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +// Compresses an image to a compressed PNG file in memory. +// On entry: +// pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. +// The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. +// level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL +// If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). +// On return: +// Function returns a pointer to the compressed data, or NULL on failure. +// *pLen_out will be set to the size of the PNG image file. +// The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + +// Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); + +// tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; + +// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). +#if TDEFL_LESS_MEMORY +enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; +#else +enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; +#endif + +// The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. +typedef enum +{ + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1, +} tdefl_status; + +// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums +typedef enum +{ + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +// tdefl's compression state structure. +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +// Initializes the compressor. +// There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. +// pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. +// If pBut_buf_func is NULL the user should always call the tdefl_compress() API. +// flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +// Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); + +// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. +// tdefl_compress_buffer() always consumes the entire input buffer. +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +// Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros. +#ifndef MINIZ_NO_ZLIB_APIS +// Create tdefl_compress() flags given zlib-style compression parameters. +// level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) +// window_bits may be -15 (raw deflate) or 15 (zlib) +// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); +#endif // #ifndef MINIZ_NO_ZLIB_APIS + +#ifdef __cplusplus +} +#endif + +#endif // MINIZ_HEADER_INCLUDED + +// ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.) + +#ifndef MINIZ_HEADER_FILE_ONLY + +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16)==2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32)==4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1]; + +#include +#include + +#define MZ_ASSERT(x) assert(x) + +#ifdef MINIZ_NO_MALLOC + #define MZ_MALLOC(x) NULL + #define MZ_FREE(x) (void)x, ((void)0) + #define MZ_REALLOC(p, x) NULL +#else + #define MZ_MALLOC(x) malloc(x) + #define MZ_FREE(x) free(x) + #define MZ_REALLOC(p, x) realloc(p, x) +#endif + +#define MZ_MAX(a,b) (((a)>(b))?(a):(b)) +#define MZ_MIN(a,b) (((a)<(b))?(a):(b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) + #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else + #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) + #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) +#endif + +#ifdef _MSC_VER + #define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) + #define MZ_FORCEINLINE inline __attribute__((__always_inline__)) +#else + #define MZ_FORCEINLINE inline +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +// ------------------- zlib-style API's + +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) +{ + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; + if (!ptr) return MZ_ADLER32_INIT; + while (buf_len) { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { + s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; + } + for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; + } + return (s2 << 16) + s1; +} + +// Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) +{ + static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; + mz_uint32 crcu32 = (mz_uint32)crc; + if (!ptr) return MZ_CRC32_INIT; + crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } + return ~crcu32; +} + +void mz_free(void *p) +{ + MZ_FREE(p); +} + +#ifndef MINIZ_NO_ZLIB_APIS + +static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } +static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } +static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } + +const char *mz_version(void) +{ + return MZ_VERSION; +} + +int mz_deflateInit(mz_streamp pStream, int level) +{ + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); +} + +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) +{ + tdefl_compressor *pComp; + mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); + + if (!pStream) return MZ_STREAM_ERROR; + if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) pStream->zalloc = def_alloc_func; + if (!pStream->zfree) pStream->zfree = def_free_func; + + pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) + { + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; + } + + return MZ_OK; +} + +int mz_deflateReset(mz_streamp pStream) +{ + if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; + pStream->total_in = pStream->total_out = 0; + tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags); + return MZ_OK; +} + +int mz_deflate(mz_streamp pStream, int flush) +{ + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; + if (!pStream->avail_out) return MZ_BUF_ERROR; + + if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; + + if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; + + orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; + for ( ; ; ) + { + tdefl_status defl_status; + in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state); + + pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) + { + mz_status = MZ_STREAM_ERROR; + break; + } + else if (defl_status == TDEFL_STATUS_DONE) + { + mz_status = MZ_STREAM_END; + break; + } + else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) + { + if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; // Can't make forward progress without some input. + } + } + return mz_status; +} + +int mz_deflateEnd(mz_streamp pStream) +{ + if (!pStream) return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) +{ + (void)pStream; + // This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) + return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +} + +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) +{ + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); + + // In case mz_ulong is 64-bits (argh I hate longs). + if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) return status; + + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; + } + + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); +} + +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); +} + +mz_ulong mz_compressBound(mz_ulong source_len) +{ + return mz_deflateBound(NULL, source_len); +} + +typedef struct +{ + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +int mz_inflateInit2(mz_streamp pStream, int window_bits) +{ + inflate_state *pDecomp; + if (!pStream) return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + if (!pStream->zalloc) pStream->zalloc = def_alloc_func; + if (!pStream->zfree) pStream->zfree = def_free_func; + + pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); + if (!pDecomp) return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +int mz_inflateInit(mz_streamp pStream) +{ + return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); +} + +int mz_inflate(mz_streamp pStream, int flush) +{ + inflate_state* pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; + + pState = (inflate_state*)pStream->state; + if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; pState->m_first_call = 0; + if (pState->m_last_status < 0) return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) + { + // MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) + { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + // flush != MZ_FINISH then we must assume there's more input. + if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; + + if (pState->m_dict_avail) + { + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; + pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; + } + + for ( ; ; ) + { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; + pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. + else if (flush == MZ_FINISH) + { + // The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } + else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) + break; + } + + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; +} + +int mz_inflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); + + // In case mz_ulong is 64-bits (argh I hate longs). + if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_inflateInit(&stream); + if (status != MZ_OK) + return status; + + status = mz_inflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; + } + *pDest_len = stream.total_out; + + return mz_inflateEnd(&stream); +} + +const char *mz_error(int err) +{ + static struct { int m_err; const char *m_pDesc; } s_error_descs[] = + { + { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, + { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } + }; + mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc; + return NULL; +} + +#endif //MINIZ_NO_ZLIB_APIS + +// ------------------- Low-level Decompression (completely independent from all compression API's) + +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) + +#define TINFL_CR_BEGIN switch(r->m_state) { case 0: +#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END +#define TINFL_CR_FINISH } + +// TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never +// reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. +#define TINFL_GET_BYTE(state_index, c) do { \ + if (pIn_buf_cur >= pIn_buf_end) { \ + for ( ; ; ) { \ + if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ + TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ + if (pIn_buf_cur < pIn_buf_end) { \ + c = *pIn_buf_cur++; \ + break; \ + } \ + } else { \ + c = 0; \ + break; \ + } \ + } \ + } else c = *pIn_buf_cur++; } MZ_MACRO_END + +#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n)) +#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END +#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END + +// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. +// It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a +// Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the +// bit buffer contains >=15 bits (deflate's max. Huffman code size). +#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ + do { \ + temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) \ + break; \ + } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \ + } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \ + } while (num_bits < 15); + +// TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read +// beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully +// decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. +// The slow path is only executed at the very end of the input buffer. +#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \ + int temp; mz_uint code_len, c; \ + if (num_bits < 15) { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) { \ + TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ + } else { \ + bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \ + } \ + } \ + if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + code_len = temp >> 9, temp &= 511; \ + else { \ + code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \ + } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END + +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) +{ + static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; + static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + static const int s_min_table_sizes[3] = { 257, 1, 4 }; + + tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; + size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; + + // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). + if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } + + num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); + if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); + if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } + } + + do + { + TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; + if (r->m_type == 0) + { + TINFL_SKIP_BITS(5, num_bits & 7); + for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } + if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } + while ((counter) && (num_bits)) + { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) + { + size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } + while (pIn_buf_cur >= pIn_buf_end) + { + if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) + { + TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); + } + else + { + TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); + } + } + n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; + } + } + else if (r->m_type == 3) + { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } + else + { + if (r->m_type == 1) + { + mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; + r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); + for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; + } + else + { + for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } + MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } + r->m_table_sizes[2] = 19; + } + for ( ; (int)r->m_type >= 0; r->m_type--) + { + int tree_next, tree_cur; tinfl_huff_table *pTable; + mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; + used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } + if ((65536 != total) && (used_syms > 1)) + { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) + { + mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; + cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); + if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } + if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) + { + tree_cur -= ((rev_code >>= 1) & 1); + if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1]; + } + tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) + { + for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); ) + { + mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } + if ((dist == 16) && (!counter)) + { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) + { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); + } + } + for ( ; ; ) + { + mz_uint8 *pSrc; + for ( ; ; ) + { + if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) + { + TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); + if (counter >= 256) + break; + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = (mz_uint8)counter; + } + else + { + int sym2; mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } +#else + if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); + } + counter = sym2; bit_buf >>= code_len; num_bits -= code_len; + if (counter & 256) + break; + +#if !TINFL_USE_64BIT_BITBUF + if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); + } + bit_buf >>= code_len; num_bits -= code_len; + + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) + { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) break; + + num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; + if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } + + TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); + num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; + if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } + + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + + pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); + + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) + { + while (counter--) + { + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) + { + const mz_uint8 *pSrc_end = pSrc + (counter & ~7); + do + { + ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; + ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) + { + if (counter) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + do + { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; pSrc += 3; + } while ((int)(counter -= 3) > 2); + if ((int)counter > 0) + { + pOut_buf_cur[0] = pSrc[0]; + if ((int)counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } + } + } while (!(r->m_final & 1)); + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } + } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); + TINFL_CR_FINISH + +common_exit: + r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) + { + const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; + } + for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; + } + r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; + } + return status; +} + +// Higher level helper functions. +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; + *pOut_len = 0; + tinfl_init(&decomp); + for ( ; ; ) + { + size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) + { + MZ_FREE(pBuf); *pOut_len = 0; return NULL; + } + src_buf_ofs += src_buf_size; + *pOut_len += dst_buf_size; + if (status == TINFL_STATUS_DONE) break; + new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; + pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); + if (!pNew_buf) + { + MZ_FREE(pBuf); *pOut_len = 0; return NULL; + } + pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; + } + return pBuf; +} + +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); + status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; +} + +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + int result = 0; + tinfl_decompressor decomp; + mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; + if (!pDict) + return TINFL_STATUS_FAILED; + tinfl_init(&decomp); + for ( ; ; ) + { + size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + in_buf_ofs += in_buf_size; + if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) + break; + if (status != TINFL_STATUS_HAS_MORE_OUTPUT) + { + result = (status == TINFL_STATUS_DONE); + break; + } + dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); + } + MZ_FREE(pDict); + *pIn_buf_size = in_buf_ofs; + return result; +} + +// ------------------- Low-level Compression (independent from all decompression API's) + +// Purposely making these tables static for faster init and thread safety. +static const mz_uint16 s_tdefl_len_sym[256] = { + 257,258,259,260,261,262,263,264,265,265,266,266,267,267,268,268,269,269,269,269,270,270,270,270,271,271,271,271,272,272,272,272, + 273,273,273,273,273,273,273,273,274,274,274,274,274,274,274,274,275,275,275,275,275,275,275,275,276,276,276,276,276,276,276,276, + 277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, + 279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280, + 281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281, + 282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282, + 283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283, + 284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,285 }; + +static const mz_uint8 s_tdefl_len_extra[256] = { + 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0 }; + +static const mz_uint8 s_tdefl_small_dist_sym[512] = { + 0,1,2,3,4,4,5,5,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 }; + +static const mz_uint8 s_tdefl_small_dist_extra[512] = { + 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7 }; + +static const mz_uint8 s_tdefl_large_dist_sym[128] = { + 0,0,18,19,20,20,21,21,22,22,22,22,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, + 28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 }; + +static const mz_uint8 s_tdefl_large_dist_extra[128] = { + 0,0,8,8,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 }; + +// Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. +typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; +static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1) +{ + mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist); + for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) + { + const mz_uint32* pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } + for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; + { tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } + } + return pCur_syms; +} + +// tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) +{ + int root, leaf, next, avbl, used, dpth; + if (n==0) return; else if (n==1) { A[0].m_key = 1; return; } + A[0].m_key += A[1].m_key; root = 0; leaf = 2; + for (next=1; next < n-1; next++) + { + if (leaf>=n || A[root].m_key=n || (root=0; next--) A[next].m_key = A[A[next].m_key].m_key+1; + avbl = 1; used = dpth = 0; root = n-2; next = n-1; + while (avbl>0) + { + while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; } + while (avbl>used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } + avbl = 2*used; dpth++; used = 0; + } +} + +// Limits canonical Huffman code table's max code size. +enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; +static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) +{ + int i; mz_uint32 total = 0; if (code_list_len <= 1) return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) + { + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } + total--; + } +} + +static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) +{ + int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes); + if (static_table) + { + for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; + } + else + { + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; + int num_used_syms = 0; + const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; + for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; } + + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); + + for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; + + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); + + MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); + } + + next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); + + for (i = 0; i < table_len; i++) + { + mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; + code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } +} + +#define TDEFL_PUT_BITS(b, l) do { \ + mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \ + while (d->m_bits_in >= 8) { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ +} MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \ + if (rle_repeat_count < 3) { \ + d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } else { \ + d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); packed_code_sizes[num_packed_code_sizes++] = 16; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ +} rle_repeat_count = 0; } } + +#define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \ + if (rle_z_count < 3) { \ + d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \ + } else if (rle_z_count <= 10) { \ + d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); packed_code_sizes[num_packed_code_sizes++] = 17; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ + } else { \ + d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); packed_code_sizes[num_packed_code_sizes++] = 18; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ +} rle_z_count = 0; } } + +static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + +static void tdefl_start_dynamic_block(tdefl_compressor *d) +{ + int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; + mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; + + d->m_huff_count[0][256] = 1; + + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; + + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; + + memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) + { + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size; + } + else if (++rle_repeat_count == 6) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + } + prev_code_size = code_size; + } + if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } + + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); + + TDEFL_PUT_BITS(2, 2); + + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); + + for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); + + for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; ) + { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); + } +} + +static void tdefl_start_static_block(tdefl_compressor *d) +{ + mz_uint i; + mz_uint8 *p = &d->m_huff_code_sizes[0][0]; + + for (i = 0; i <= 143; ++i) *p++ = 8; + for ( ; i <= 255; ++i) *p++ = 9; + for ( ; i <= 279; ++i) *p++ = 7; + for ( ; i <= 287; ++i) *p++ = 8; + + memset(d->m_huff_code_sizes[1], 5, 32); + + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); + + TDEFL_PUT_BITS(1, 2); +} + +static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + mz_uint8 *pOutput_buf = d->m_pOutput_buf; + mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); } + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + + if (flags & 1) + { + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + // This sequence coaxes MSVC into using cmov's vs. jmp's. + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; + + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + } + + if (pOutput_buf >= d->m_pOutput_buf_end) + return MZ_FALSE; + + *(mz_uint64*)pOutput_buf = bit_buffer; + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; + } + +#undef TDEFL_PUT_BITS_FAST + + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; + + while (bits_in) + { + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + if (flags & 1) + { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + if (match_dist < 512) + { + sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; + } + else + { + sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; + } + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS + +static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) +{ + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} + +static int tdefl_flush_block(tdefl_compressor *d, int flush) +{ + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8 *pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; + mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; + + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; + + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; + + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); + + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) + { + TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8); + } + + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); + + pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; + + if (!use_raw_block) + comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); + + // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. + if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) ) + { + mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) + { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) + { + TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); + } + } + // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. + else if (!comp_block_succeeded) + { + d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); + } + + if (flush) + { + if (flush == TDEFL_FINISH) + { + if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } + if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } } + } + else + { + mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); } + } + } + + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++; + + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) + { + if (d->m_pPut_buf_func) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } + else if (pOutput_buf_start == d->m_output_buf) + { + int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) + { + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; + } + } + else + { + d->m_out_buf_ofs += n; + } + } + + return d->m_output_flush_remaining; +} + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p) +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s); + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; + for ( ; ; ) + { + for ( ; ; ) + { + if (--num_probes_left == 0) return; + #define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break; + TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; + } + if (!dist) break; q = (const mz_uint16*)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD(q) != s01) continue; p = s; probe_len = 32; + do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && + (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); + if (!probe_len) + { + *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break; + } + else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len) + { + *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } +} +#else +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; + for ( ; ; ) + { + for ( ; ; ) + { + if (--num_probes_left == 0) return; + #define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break; + TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; + } + if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; + if (probe_len > match_len) + { + *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; + c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; + } + } +} +#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +static mz_bool tdefl_compress_fast(tdefl_compressor *d) +{ + // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. + mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; + mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; + mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + + while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) + { + const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; + mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); + d->m_src_buf_left -= num_bytes_to_process; + lookahead_size += num_bytes_to_process; + + while (num_bytes_to_process) + { + mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); + memcpy(d->m_dict + dst_pos, d->m_pSrc, n); + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); + d->m_pSrc += n; + dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; + num_bytes_to_process -= n; + } + + dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); + if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; + + while (lookahead_size >= 4) + { + mz_uint cur_match_dist, cur_match_len = 1; + mz_uint8 *pCur_dict = d->m_dict + cur_pos; + mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; + mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; + mz_uint probe_pos = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)lookahead_pos; + + if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) + { + const mz_uint16 *p = (const mz_uint16 *)pCur_dict; + const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); + mz_uint32 probe_len = 32; + do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && + (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); + cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); + if (!probe_len) + cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + + if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U))) + { + cur_match_len = 1; + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + else + { + mz_uint32 s0, s1; + cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + + MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + + cur_match_dist--; + + pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); + *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; + pLZ_code_buf += 3; + *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + + s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; + s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; + d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; + } + } + else + { + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + + if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } + + total_lz_bytes += cur_match_len; + lookahead_pos += cur_match_len; + dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; + MZ_ASSERT(lookahead_size >= cur_match_len); + lookahead_size -= cur_match_len; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; + } + } + + while (lookahead_size) + { + mz_uint8 lit = d->m_dict[cur_pos]; + + total_lz_bytes++; + *pLZ_code_buf++ = lit; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } + + d->m_huff_count[0][lit]++; + + lookahead_pos++; + dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + lookahead_size--; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; + } + } + } + + d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; + return MZ_TRUE; +} +#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + +static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) +{ + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } + d->m_huff_count[0][lit]++; +} + +static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) +{ + mz_uint32 s0, s1; + + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); + + d->m_total_lz_bytes += match_len; + + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); + + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; + + *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + + if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; +} + +static mz_bool tdefl_compress_normal(tdefl_compressor *d) +{ + const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) + { + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) + { + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; + mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) + { + mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; + } + } + else + { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) + { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + break; + + // Simple lazy/greedy parsing state machine. + len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) + { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) + { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; + } + } + else + { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); + } + if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) + { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) + { + if (cur_match_len > d->m_saved_match_len) + { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; + } + } + else + { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; + } + } + else if (!cur_match_dist) + tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; + } + // Move the lookahead forward by len_to_move bytes. + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE); + // Check if it's time to flush the current LZ codes to the internal output buffer. + if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) ) + { + int n; + d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + } + } + + d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; + return MZ_TRUE; +} + +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) +{ + if (d->m_pIn_buf_size) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + } + + if (d->m_pOut_buf_size) + { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; + + *d->m_pOut_buf_size = d->m_out_buf_ofs; + } + + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) +{ + if (!d) + { + if (pIn_buf_size) *pIn_buf_size = 0; + if (pOut_buf_size) *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; + } + + d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if ( ((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) ) + { + if (pIn_buf_size) *pIn_buf_size = 0; + if (pOut_buf_size) *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); + } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); + + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && + ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && + ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) + { + if (!tdefl_compress_fast(d)) + return d->m_prev_return_status; + } + else +#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; + } + + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) + d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); + + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) + { + if (tdefl_flush_block(d, flush) < 0) + return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; } + } + + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); +} + +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) +{ + MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); +} + +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; + d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash); + d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; + d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; + d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; + d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) +{ + return d->m_prev_return_status; +} + +mz_uint32 tdefl_get_adler32(tdefl_compressor *d) +{ + return d->m_adler32; +} + +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; + pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; + succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); + succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); + MZ_FREE(pComp); return succeeded; +} + +typedef struct +{ + size_t m_size, m_capacity; + mz_uint8 *m_pBuf; + mz_bool m_expandable; +} tdefl_output_buffer; + +static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) +{ + tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; + size_t new_size = p->m_size + len; + if (new_size > p->m_capacity) + { + size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; + do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); + pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; + p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; + } + memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; + return MZ_TRUE; +} + +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); + if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; + out_buf.m_expandable = MZ_TRUE; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; + *pOut_len = out_buf.m_size; return out_buf.m_pBuf; +} + +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); + if (!pOut_buf) return 0; + out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; + return out_buf.m_size; +} + +#ifndef MINIZ_NO_ZLIB_APIS +static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + +// level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) +{ + mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} +#endif //MINIZ_NO_ZLIB_APIS + +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) +#endif + +// Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at +// http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. +// This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) +{ + // Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. + static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0; + if (!pComp) return NULL; + MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57+MZ_MAX(64, (1+bpl)*h); if (NULL == (out_buf.m_pBuf = (mz_uint8*)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; } + // write dummy header + for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); + // compress image data + tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); + for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); } + if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } + // write real header + *pLen_out = out_buf.m_size-41; + { + static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06}; + mz_uint8 pnghdr[41]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, + 0,0,(mz_uint8)(w>>8),(mz_uint8)w,0,0,(mz_uint8)(h>>8),(mz_uint8)h,8,chans[num_chans],0,0,0,0,0,0,0, + (mz_uint8)(*pLen_out>>24),(mz_uint8)(*pLen_out>>16),(mz_uint8)(*pLen_out>>8),(mz_uint8)*pLen_out,0x49,0x44,0x41,0x54}; + c=(mz_uint32)mz_crc32(MZ_CRC32_INIT,pnghdr+12,17); for (i=0; i<4; ++i, c<<=8) ((mz_uint8*)(pnghdr+29))[i]=(mz_uint8)(c>>24); + memcpy(out_buf.m_pBuf, pnghdr, 41); + } + // write footer (IDAT CRC-32, followed by IEND chunk) + if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT,out_buf.m_pBuf+41-4, *pLen_out+4); for (i=0; i<4; ++i, c<<=8) (out_buf.m_pBuf+out_buf.m_size-16)[i] = (mz_uint8)(c >> 24); + // compute final size of file, grab compressed data buffer and return + *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; +} +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) +{ + // Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) + return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); +} + +#ifdef _MSC_VER +#pragma warning (pop) +#endif + +// ------------------- .ZIP archive reading + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef MINIZ_NO_STDIO + #define MZ_FILE void * +#else + #include + #include + + #if defined(_MSC_VER) || defined(__MINGW64__) + static FILE *mz_fopen(const char *pFilename, const char *pMode) + { + FILE* pFile = NULL; + fopen_s(&pFile, pFilename, pMode); + return pFile; + } + static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) + { + FILE* pFile = NULL; + if (freopen_s(&pFile, pPath, pMode, pStream)) + return NULL; + return pFile; + } + #ifndef MINIZ_NO_TIME + #include + #endif + #define MZ_FILE FILE + #define MZ_FOPEN mz_fopen + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 _ftelli64 + #define MZ_FSEEK64 _fseeki64 + #define MZ_FILE_STAT_STRUCT _stat + #define MZ_FILE_STAT _stat + #define MZ_FFLUSH fflush + #define MZ_FREOPEN mz_freopen + #define MZ_DELETE_FILE remove + #elif defined(__MINGW32__) + #ifndef MINIZ_NO_TIME + #include + #endif + #define MZ_FILE FILE + #define MZ_FOPEN(f, m) fopen(f, m) + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 ftello64 + #define MZ_FSEEK64 fseeko64 + #define MZ_FILE_STAT_STRUCT _stat + #define MZ_FILE_STAT _stat + #define MZ_FFLUSH fflush + #define MZ_FREOPEN(f, m, s) freopen(f, m, s) + #define MZ_DELETE_FILE remove + #elif defined(__TINYC__) + #ifndef MINIZ_NO_TIME + #include + #endif + #define MZ_FILE FILE + #define MZ_FOPEN(f, m) fopen(f, m) + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 ftell + #define MZ_FSEEK64 fseek + #define MZ_FILE_STAT_STRUCT stat + #define MZ_FILE_STAT stat + #define MZ_FFLUSH fflush + #define MZ_FREOPEN(f, m, s) freopen(f, m, s) + #define MZ_DELETE_FILE remove + #elif defined(__GNUC__) && _LARGEFILE64_SOURCE + #ifndef MINIZ_NO_TIME + #include + #endif + #define MZ_FILE FILE + #define MZ_FOPEN(f, m) fopen64(f, m) + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 ftello64 + #define MZ_FSEEK64 fseeko64 + #define MZ_FILE_STAT_STRUCT stat64 + #define MZ_FILE_STAT stat64 + #define MZ_FFLUSH fflush + #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) + #define MZ_DELETE_FILE remove + #else + #ifndef MINIZ_NO_TIME + #include + #endif + #define MZ_FILE FILE + #define MZ_FOPEN(f, m) fopen(f, m) + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 ftello + #define MZ_FSEEK64 fseeko + #define MZ_FILE_STAT_STRUCT stat + #define MZ_FILE_STAT stat + #define MZ_FFLUSH fflush + #define MZ_FREOPEN(f, m, s) freopen(f, m, s) + #define MZ_DELETE_FILE remove + #endif // #ifdef _MSC_VER +#endif // #ifdef MINIZ_NO_STDIO + +#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +// Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. +enum +{ + // ZIP archive identifiers and record sizes + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + // Central directory header record offsets + MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8, + MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16, + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, + MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, + // Local directory header offsets + MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10, + MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, + MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, + // End of central directory offsets + MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, +}; + +typedef struct +{ + void *m_p; + size_t m_size, m_capacity; + mz_uint m_element_size; +} mz_zip_array; + +struct mz_zip_internal_state_tag +{ + mz_zip_array m_central_dir; + mz_zip_array m_central_dir_offsets; + mz_zip_array m_sorted_central_dir_offsets; + MZ_FILE *m_pFile; + void *m_pMem; + size_t m_mem_size; + size_t m_mem_capacity; +}; + +#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] + +static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) +{ + pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); + memset(pArray, 0, sizeof(mz_zip_array)); +} + +static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) +{ + void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE; + if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; } + if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE; + pArray->m_p = pNew_p; pArray->m_capacity = new_capacity; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) +{ + if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; } + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) +{ + if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; } + pArray->m_size = new_size; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) +{ + return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) +{ + size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; + memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; + tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31; + tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62; + return mktime(&tm); +} + +static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ +#ifdef _MSC_VER + struct tm tm_struct; + struct tm *tm = &tm_struct; + errno_t err = localtime_s(tm, &time); + if (err) + { + *pDOS_date = 0; *pDOS_time = 0; + return; + } +#else + struct tm *tm = localtime(&time); +#endif + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); + *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +} +#endif + +#ifndef MINIZ_NO_STDIO +static mz_bool mz_zip_get_file_modified_time(const char *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ +#ifdef MINIZ_NO_TIME + (void)pFilename; *pDOS_date = *pDOS_time = 0; +#else + struct MZ_FILE_STAT_STRUCT file_stat; + // On Linux with x86 glibc, this call will fail on large files (>= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. + if (MZ_FILE_STAT(pFilename, &file_stat) != 0) + return MZ_FALSE; + mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date); +#endif // #ifdef MINIZ_NO_TIME + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time) +{ + struct utimbuf t; t.actime = access_time; t.modtime = modified_time; + return !utime(pFilename, &t); +} +#endif // #ifndef MINIZ_NO_TIME +#endif // #ifndef MINIZ_NO_STDIO + +static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags) +{ + (void)flags; + if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return MZ_FALSE; + + if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; + if (!pZip->m_pFree) pZip->m_pFree = def_free_func; + if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; + + pZip->m_zip_mode = MZ_ZIP_MODE_READING; + pZip->m_archive_size = 0; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return MZ_FALSE; + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; pR++; + } + return (pL == pE) ? (l_len < r_len) : (l < r); +} + +#define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END + +// Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) +static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const int size = pZip->m_total_files; + int start = (size - 2) >> 1, end; + while (start >= 0) + { + int child, root = start; + for ( ; ; ) + { + if ((child = (root << 1) + 1) >= size) + break; + child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]))); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; + } + start--; + } + + end = size - 1; + while (end > 0) + { + int child, root = 0; + MZ_SWAP_UINT32(pIndices[end], pIndices[0]); + for ( ; ; ) + { + if ((child = (root << 1) + 1) >= end) + break; + child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; + } + end--; + } +} + +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags) +{ + mz_uint cdir_size, num_this_disk, cdir_disk_index; + mz_uint64 cdir_ofs; + mz_int64 cur_file_ofs; + const mz_uint8 *p; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); + // Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return MZ_FALSE; + // Find the end of central directory record by scanning the file from the end towards the beginning. + cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); + for ( ; ; ) + { + int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) + return MZ_FALSE; + for (i = n - 4; i >= 0; --i) + if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + break; + if (i >= 0) + { + cur_file_ofs += i; + break; + } + if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) + return MZ_FALSE; + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); + } + // Read and verify the end of central directory record. + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) || + ((pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS))) + return MZ_FALSE; + + num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); + cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) + return MZ_FALSE; + + if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return MZ_FALSE; + + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) + return MZ_FALSE; + + pZip->m_central_directory_file_ofs = cdir_ofs; + + if (pZip->m_total_files) + { + mz_uint i, n; + + // Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices. + if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) + return MZ_FALSE; + + if (sort_central_dir) + { + if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) + return MZ_FALSE; + } + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) + return MZ_FALSE; + + // Now create an index into the central directory file records, do some basic sanity checking on each record, and check for zip64 entries (which are not yet supported). + p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; + for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) + { + mz_uint total_header_size, comp_size, decomp_size, disk_index; + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) + return MZ_FALSE; + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); + if (sort_central_dir) + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF)) + return MZ_FALSE; + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); + if ((disk_index != num_this_disk) && (disk_index != 1)) + return MZ_FALSE; + if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) + return MZ_FALSE; + if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) + return MZ_FALSE; + n -= total_header_size; p += total_header_size; + } + } + + if (sort_central_dir) + mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags) +{ + if ((!pZip) || (!pZip->m_pRead)) + return MZ_FALSE; + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + pZip->m_archive_size = size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} + +static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); + memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); + return s; +} + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags) +{ + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + pZip->m_archive_size = size; + pZip->m_pRead = mz_zip_mem_read_func; + pZip->m_pIO_opaque = pZip; +#ifdef __cplusplus + pZip->m_pState->m_pMem = const_cast(pMem); +#else + pZip->m_pState->m_pMem = (void *)pMem; +#endif + pZip->m_pState->m_mem_size = size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) +{ + mz_uint64 file_size; + MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb"); + if (!pFile) + return MZ_FALSE; + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + file_size = MZ_FTELL64(pFile); + if (!mz_zip_reader_init_internal(pZip, flags)) + { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + pZip->m_pRead = mz_zip_file_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = file_size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_total_files : 0; +} + +static MZ_FORCEINLINE const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index) +{ + if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return NULL; + return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); +} + +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint m_bit_flag; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) + return MZ_FALSE; + m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + return (m_bit_flag & 1); +} + +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint filename_len, external_attr; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) + return MZ_FALSE; + + // First see if the filename ends with a '/' character. + filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_len) + { + if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') + return MZ_TRUE; + } + + // Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. + // Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. + // FIXME: Remove this check? Is it necessary - we already check the filename. + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((external_attr & 0x10) != 0) + return MZ_TRUE; + + return MZ_FALSE; +} + +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if ((!p) || (!pStat)) + return MZ_FALSE; + + // Unpack the central directory record. + pStat->m_file_index = file_index; + pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); + pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); + pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); + pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +#ifndef MINIZ_NO_TIME + pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); +#endif + pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); + pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + + // Copy as much of the filename and comment as possible. + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); + memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0'; + + n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); + pStat->m_comment_size = n; + memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0'; + + return MZ_TRUE; +} + +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; } + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_buf_size) + { + n = MZ_MIN(n, filename_buf_size - 1); + memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pFilename[n] = '\0'; + } + return n + 1; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) +{ + mz_uint i; + if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) + return 0 == memcmp(pA, pB, len); + for (i = 0; i < len; ++i) + if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) + return MZ_FALSE; + return MZ_TRUE; +} + +static MZ_FORCEINLINE int mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; pR++; + } + return (pL == pE) ? (int)(l_len - r_len) : (l - r); +} + +static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const int size = pZip->m_total_files; + const mz_uint filename_len = (mz_uint)strlen(pFilename); + int l = 0, h = size - 1; + while (l <= h) + { + int m = (l + h) >> 1, file_index = pIndices[m], comp = mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); + if (!comp) + return file_index; + else if (comp < 0) + l = m + 1; + else + h = m - 1; + } + return -1; +} + +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) +{ + mz_uint file_index; size_t name_len, comment_len; + if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return -1; + if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) + return mz_zip_reader_locate_file_binary_search(pZip, pName); + name_len = strlen(pName); if (name_len > 0xFFFF) return -1; + comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1; + for (file_index = 0; file_index < pZip->m_total_files; file_index++) + { + const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); + mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + if (filename_len < name_len) + continue; + if (comment_len) + { + mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); + const char *pFile_comment = pFilename + filename_len + file_extra_len; + if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags))) + continue; + } + if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) + { + int ofs = filename_len - 1; + do + { + if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) + break; + } while (--ofs >= 0); + ofs++; + pFilename += ofs; filename_len -= ofs; + } + if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags))) + return file_index; + } + return -1; +} + +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + int status = TINFL_STATUS_DONE; + mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + tinfl_decompressor inflator; + + if ((buf_size) && (!pBuf)) + return MZ_FALSE; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) + if (!file_stat.m_comp_size) + return MZ_TRUE; + + // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). + // I'm torn how to handle this case - should it fail instead? + if (mz_zip_reader_is_file_a_directory(pZip, file_index)) + return MZ_TRUE; + + // Encryption and patch files are not supported. + if (file_stat.m_bit_flag & (1 | 32)) + return MZ_FALSE; + + // This function only supports stored and deflate. + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return MZ_FALSE; + + // Ensure supplied output buffer is large enough. + needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (buf_size < needed_size) + return MZ_FALSE; + + // Read and parse the local directory entry. + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return MZ_FALSE; + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + // The file is stored or the caller has requested the compressed data. + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) + return MZ_FALSE; + return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32); + } + + // Decompress the file either directly from memory or from a file input buffer. + tinfl_init(&inflator); + + if (pZip->m_pState->m_pMem) + { + // Read directly from the archive in memory. + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else if (pUser_read_buf) + { + // Use a user provided read buffer. + if (!user_read_buf_size) + return MZ_FALSE; + pRead_buf = (mz_uint8 *)pUser_read_buf; + read_buf_size = user_read_buf_size; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + else + { + // Temporarily allocate a read buffer. + read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); +#ifdef _MSC_VER + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) +#else + if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) +#endif + return MZ_FALSE; + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return MZ_FALSE; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + do + { + size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + out_buf_ofs += out_buf_size; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + + if (status == TINFL_STATUS_DONE) + { + // Make sure the entire file was decompressed, and check its CRC. + if ((out_buf_ofs != file_stat.m_uncomp_size) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)) + status = TINFL_STATUS_FAILED; + } + + if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); +} + +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); +} + +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); +} + +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) +{ + mz_uint64 comp_size, uncomp_size, alloc_size; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + void *pBuf; + + if (pSize) + *pSize = 0; + if (!p) + return NULL; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; +#ifdef _MSC_VER + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) +#else + if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) +#endif + return NULL; + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) + return NULL; + + if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return NULL; + } + + if (pSize) *pSize = (size_t)alloc_size; + return pBuf; +} + +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) +{ + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + { + if (pSize) *pSize = 0; + return MZ_FALSE; + } + return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +} + +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT; + mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf = NULL; void *pWrite_buf = NULL; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) + if (!file_stat.m_comp_size) + return MZ_TRUE; + + // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). + // I'm torn how to handle this case - should it fail instead? + if (mz_zip_reader_is_file_a_directory(pZip, file_index)) + return MZ_TRUE; + + // Encryption and patch files are not supported. + if (file_stat.m_bit_flag & (1 | 32)) + return MZ_FALSE; + + // This function only supports stored and deflate. + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return MZ_FALSE; + + // Read and parse the local directory entry. + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return MZ_FALSE; + + // Decompress the file either directly from memory or from a file input buffer. + if (pZip->m_pState->m_pMem) + { + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return MZ_FALSE; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + // The file is stored or the caller has requested the compressed data. + if (pZip->m_pState->m_pMem) + { +#ifdef _MSC_VER + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) +#else + if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) +#endif + return MZ_FALSE; + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) + status = TINFL_STATUS_FAILED; + else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); + cur_file_ofs += file_stat.m_comp_size; + out_buf_ofs += file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + while (comp_remaining) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + + if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + out_buf_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + } + } + } + else + { + tinfl_decompressor inflator; + tinfl_init(&inflator); + + if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + status = TINFL_STATUS_FAILED; + else + { + do + { + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + + if (out_buf_size) + { + if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) + { + status = TINFL_STATUS_FAILED; + break; + } + file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); + if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) + { + status = TINFL_STATUS_FAILED; + break; + } + } + } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); + } + } + + if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + // Make sure the entire file was decompressed, and check its CRC. + if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32)) + status = TINFL_STATUS_FAILED; + } + + if (!pZip->m_pState->m_pMem) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + if (pWrite_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) +{ + (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque); +} + +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) +{ + mz_bool status; + mz_zip_archive_file_stat file_stat; + MZ_FILE *pFile; + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + pFile = MZ_FOPEN(pDst_filename, "wb"); + if (!pFile) + return MZ_FALSE; + status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); + if (MZ_FCLOSE(pFile) == EOF) + return MZ_FALSE; +#ifndef MINIZ_NO_TIME + if (status) + mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); +#endif + return status; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_reader_end(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return MZ_FALSE; + + if (pZip->m_pState) + { + mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + MZ_FCLOSE(pState->m_pFile); + pState->m_pFile = NULL; + } +#endif // #ifndef MINIZ_NO_STDIO + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + } + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) +{ + int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +} +#endif + +// ------------------- .ZIP archive writing + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); } +static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); } +#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) + +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) +{ + if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return MZ_FALSE; + + if (pZip->m_file_offset_alignment) + { + // Ensure user specified file offset alignment is a power of 2. + if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) + return MZ_FALSE; + } + + if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; + if (!pZip->m_pFree) pZip->m_pFree = def_free_func; + if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + pZip->m_archive_size = existing_size; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return MZ_FALSE; + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + return MZ_TRUE; +} + +static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); +#ifdef _MSC_VER + if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) +#else + if ((!n) || ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) +#endif + return 0; + if (new_size > pState->m_mem_capacity) + { + void *pNew_block; + size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2; + if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) + return 0; + pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity; + } + memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); + pState->m_mem_size = (size_t)new_size; + return n; +} + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) +{ + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pIO_opaque = pZip; + if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) + return MZ_FALSE; + if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) + { + if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) + { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + pZip->m_pState->m_mem_capacity = initial_allocation_size; + } + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) +{ + MZ_FILE *pFile; + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pIO_opaque = pZip; + if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) + return MZ_FALSE; + if (NULL == (pFile = MZ_FOPEN(pFilename, "wb"))) + { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + pZip->m_pState->m_pFile = pFile; + if (size_to_reserve_at_beginning) + { + mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf); + do + { + size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) + { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + cur_ofs += n; size_to_reserve_at_beginning -= n; + } while (size_to_reserve_at_beginning); + } + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) +{ + mz_zip_internal_state *pState; + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return MZ_FALSE; + // No sense in trying to write to an archive that's already at the support max size + if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) + return MZ_FALSE; + + pState = pZip->m_pState; + + if (pState->m_pFile) + { +#ifdef MINIZ_NO_STDIO + pFilename; return MZ_FALSE; +#else + // Archive is being read from stdio - try to reopen as writable. + if (pZip->m_pIO_opaque != pZip) + return MZ_FALSE; + if (!pFilename) + return MZ_FALSE; + pZip->m_pWrite = mz_zip_file_write_func; + if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) + { + // The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. + mz_zip_reader_end(pZip); + return MZ_FALSE; + } +#endif // #ifdef MINIZ_NO_STDIO + } + else if (pState->m_pMem) + { + // Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. + if (pZip->m_pIO_opaque != pZip) + return MZ_FALSE; + pState->m_mem_capacity = pState->m_mem_size; + pZip->m_pWrite = mz_zip_heap_write_func; + } + // Archive is being read via a user provided read function - make sure the user has specified a write function too. + else if (!pZip->m_pWrite) + return MZ_FALSE; + + // Start writing new files at the archive's current central directory location. + pZip->m_archive_size = pZip->m_central_directory_file_ofs; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + pZip->m_central_directory_file_ofs = 0; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) +{ + return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); +} + +typedef struct +{ + mz_zip_archive *m_pZip; + mz_uint64 m_cur_archive_file_ofs; + mz_uint64 m_comp_size; +} mz_zip_writer_add_state; + +static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser) +{ + mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; + if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) + return MZ_FALSE; + pState->m_cur_archive_file_ofs += len; + pState->m_comp_size += len; + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +{ + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; + size_t orig_central_dir_size = pState->m_central_dir.m_size; + mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + + // No zip64 support yet + if ((local_header_ofs > 0xFFFFFFFF) || (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + comment_size) > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) + return MZ_FALSE; + + if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) + { + // Try to push the central directory array back into its original state. + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) +{ + // Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. + if (*pArchive_name == '/') + return MZ_FALSE; + while (*pArchive_name) + { + if ((*pArchive_name == '\\') || (*pArchive_name == ':')) + return MZ_FALSE; + pArchive_name++; + } + return MZ_TRUE; +} + +static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) +{ + mz_uint32 n; + if (!pZip->m_file_offset_alignment) + return 0; + n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); + return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1); +} + +static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) +{ + char buf[4096]; + memset(buf, 0, MZ_MIN(sizeof(buf), n)); + while (n) + { + mz_uint32 s = MZ_MIN(sizeof(buf), n); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) + return MZ_FALSE; + cur_file_ofs += s; n -= s; + } + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) +{ + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs, cur_archive_file_ofs, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + tdefl_compressor *pComp = NULL; + mz_bool store_data_uncompressed; + mz_zip_internal_state *pState; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION)) + return MZ_FALSE; + + local_dir_header_ofs = cur_archive_file_ofs = pZip->m_archive_size; + pState = pZip->m_pState; + + if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) + return MZ_FALSE; + // No zip64 support yet + if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + +#ifndef MINIZ_NO_TIME + { + time_t cur_time; time(&cur_time); + mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date); + } +#endif // #ifndef MINIZ_NO_TIME + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > 0xFFFF) + return MZ_FALSE; + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) + return MZ_FALSE; + + if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) + { + // Set DOS Subdirectory attribute bit. + ext_attributes |= 0x10; + // Subdirectories cannot contain data. + if ((buf_size) || (uncomp_size)) + return MZ_FALSE; + } + + // Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) + if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size)) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) + return MZ_FALSE; + + if ((!store_data_uncompressed) && (buf_size)) + { + if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) + return MZ_FALSE; + } + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } + cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); + + MZ_CLEAR_OBJ(local_dir_header); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + cur_archive_file_ofs += archive_name_size; + + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) + { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + + if (store_data_uncompressed) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + cur_archive_file_ofs += buf_size; + comp_size = buf_size; + + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + method = MZ_DEFLATED; + } + else if (buf_size) + { + mz_zip_writer_add_state state; + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || + (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + + method = MZ_DEFLATED; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pComp = NULL; + + // no zip64 support yet + if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) + return MZ_FALSE; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return MZ_FALSE; + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; + mz_uint64 local_dir_header_ofs, cur_archive_file_ofs, uncomp_size = 0, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + MZ_FILE *pSrc_file = NULL; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return MZ_FALSE; + + local_dir_header_ofs = cur_archive_file_ofs = pZip->m_archive_size; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > 0xFFFF) + return MZ_FALSE; + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date)) + return MZ_FALSE; + + pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); + if (!pSrc_file) + return MZ_FALSE; + MZ_FSEEK64(pSrc_file, 0, SEEK_END); + uncomp_size = MZ_FTELL64(pSrc_file); + MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + + if (uncomp_size > 0xFFFFFFFF) + { + // No zip64 support yet + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + if (uncomp_size <= 3) + level = 0; + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) + { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } + cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); + + MZ_CLEAR_OBJ(local_dir_header); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + cur_archive_file_ofs += archive_name_size; + + if (uncomp_size) + { + mz_uint64 uncomp_remaining = uncomp_size; + void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); + if (!pRead_buf) + { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + if (!level) + { + while (uncomp_remaining) + { + mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); + if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + uncomp_remaining -= n; + cur_archive_file_ofs += n; + } + comp_size = uncomp_size; + } + else + { + mz_bool result = MZ_FALSE; + mz_zip_writer_add_state state; + tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + for ( ; ; ) + { + size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE); + tdefl_status status; + + if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) + break; + + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); + uncomp_remaining -= in_buf_size; + + status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); + if (status == TDEFL_STATUS_DONE) + { + result = MZ_TRUE; + break; + } + else if (status != TDEFL_STATUS_OKAY) + break; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + + if (!result) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + + method = MZ_DEFLATED; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + } + + MZ_FCLOSE(pSrc_file); pSrc_file = NULL; + + // no zip64 support yet + if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) + return MZ_FALSE; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return MZ_FALSE; + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index) +{ + mz_uint n, bit_flags, num_alignment_padding_bytes; + mz_uint64 comp_bytes_remaining, local_dir_header_ofs; + mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + size_t orig_central_dir_size; + mz_zip_internal_state *pState; + void *pBuf; const mz_uint8 *pSrc_central_header; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return MZ_FALSE; + if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index))) + return MZ_FALSE; + pState = pZip->m_pState; + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) + return MZ_FALSE; + + cur_src_file_ofs = MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + cur_dst_file_ofs = pZip->m_archive_size; + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) + return MZ_FALSE; + cur_dst_file_ofs += num_alignment_padding_bytes; + local_dir_header_ofs = cur_dst_file_ofs; + if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(sizeof(mz_uint32) * 4, MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining))))) + return MZ_FALSE; + + while (comp_bytes_remaining) + { + n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining); + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + cur_src_file_ofs += n; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + cur_dst_file_ofs += n; + + comp_bytes_remaining -= n; + } + + bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + if (bit_flags & 8) + { + // Copy data descriptor + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + + n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + + cur_src_file_ofs += n; + cur_dst_file_ofs += n; + } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + + // no zip64 support yet + if (cur_dst_file_ofs > 0xFFFFFFFF) + return MZ_FALSE; + + orig_central_dir_size = pState->m_central_dir.m_size; + + memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + return MZ_FALSE; + + n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return MZ_FALSE; + } + + if (pState->m_central_dir.m_size > 0xFFFFFFFF) + return MZ_FALSE; + n = (mz_uint32)orig_central_dir_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return MZ_FALSE; + } + + pZip->m_total_files++; + pZip->m_archive_size = cur_dst_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_uint64 central_dir_ofs, central_dir_size; + mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE]; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return MZ_FALSE; + + pState = pZip->m_pState; + + // no zip64 support yet + if ((pZip->m_total_files > 0xFFFF) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) + return MZ_FALSE; + + central_dir_ofs = 0; + central_dir_size = 0; + if (pZip->m_total_files) + { + // Write central directory + central_dir_ofs = pZip->m_archive_size; + central_dir_size = pState->m_central_dir.m_size; + pZip->m_central_directory_file_ofs = central_dir_ofs; + if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) + return MZ_FALSE; + pZip->m_archive_size += central_dir_size; + } + + // Write end of central directory record + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr)) + return MZ_FALSE; +#ifndef MINIZ_NO_STDIO + if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) + return MZ_FALSE; +#endif // #ifndef MINIZ_NO_STDIO + + pZip->m_archive_size += sizeof(hdr); + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize) +{ + if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize)) + return MZ_FALSE; + if (pZip->m_pWrite != mz_zip_heap_write_func) + return MZ_FALSE; + if (!mz_zip_writer_finalize_archive(pZip)) + return MZ_FALSE; + + *pBuf = pZip->m_pState->m_pMem; + *pSize = pZip->m_pState->m_mem_size; + pZip->m_pState->m_pMem = NULL; + pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_end(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_bool status = MZ_TRUE; + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) + return MZ_FALSE; + + pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + MZ_FCLOSE(pState->m_pFile); + pState->m_pFile = NULL; + } +#endif // #ifndef MINIZ_NO_STDIO + + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); + pState->m_pMem = NULL; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + return status; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + mz_bool status, created_new_archive = MZ_FALSE; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + MZ_CLEAR_OBJ(zip_archive); + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) + { + // Create a new archive. + if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0)) + return MZ_FALSE; + created_new_archive = MZ_TRUE; + } + else + { + // Append to an existing archive. + if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) + return MZ_FALSE; + if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename)) + { + mz_zip_reader_end(&zip_archive); + return MZ_FALSE; + } + } + status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); + // Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) + if (!mz_zip_writer_finalize_archive(&zip_archive)) + status = MZ_FALSE; + if (!mz_zip_writer_end(&zip_archive)) + status = MZ_FALSE; + if ((!status) && (created_new_archive)) + { + // It's a new archive and something went wrong, so just delete it. + int ignoredStatus = MZ_DELETE_FILE(pZip_filename); + (void)ignoredStatus; + } + return status; +} + +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) +{ + int file_index; + mz_zip_archive zip_archive; + void *p = NULL; + + if (pSize) + *pSize = 0; + + if ((!pZip_filename) || (!pArchive_name)) + return NULL; + + MZ_CLEAR_OBJ(zip_archive); + if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) + return NULL; + + if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0) + p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); + + mz_zip_reader_end(&zip_archive); + return p; +} + +#endif // #ifndef MINIZ_NO_STDIO + +#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +#endif // #ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +} +#endif + +#endif // MINIZ_HEADER_FILE_ONLY + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +*/ diff --git a/contrib/zip/src/zip.c b/contrib/zip/src/zip.c new file mode 100644 index 000000000..80573096b --- /dev/null +++ b/contrib/zip/src/zip.c @@ -0,0 +1,640 @@ +/* + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "zip.h" +#include "miniz.h" + +#include +#include +#include + +#if defined _WIN32 || defined __WIN32__ +/* Win32, DOS */ +#include + +#define MKDIR(DIRNAME) _mkdir(DIRNAME) +#define STRCLONE(STR) ((STR) ? _strdup(STR) : NULL) +#define HAS_DEVICE(P) \ + ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) && \ + (P)[1] == ':') +#define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE(P) ? 2 : 0) +#define ISSLASH(C) ((C) == '/' || (C) == '\\') + +#else +#define MKDIR(DIRNAME) mkdir(DIRNAME, 0755) +#define STRCLONE(STR) ((STR) ? strdup(STR) : NULL) +#endif + +#ifndef FILESYSTEM_PREFIX_LEN +#define FILESYSTEM_PREFIX_LEN(P) 0 +#endif + +#ifndef ISSLASH +#define ISSLASH(C) ((C) == '/') +#endif + +#define CLEANUP(ptr) \ + do { \ + if (ptr) { \ + free((void *)ptr); \ + ptr = NULL; \ + } \ + } while (0) + +static char *basename(const char *name) { + char const *p; + char const *base = name += FILESYSTEM_PREFIX_LEN(name); + int all_slashes = 1; + + for (p = name; *p; p++) { + if (ISSLASH(*p)) + base = p + 1; + else + all_slashes = 0; + } + + /* If NAME is all slashes, arrange to return `/'. */ + if (*base == '\0' && ISSLASH(*name) && all_slashes) --base; + + return (char *)base; +} + +static int mkpath(const char *path) { + char const *p; + char npath[MAX_PATH + 1] = {0}; + int len = 0; + + for (p = path; *p && len < MAX_PATH; p++) { + if (ISSLASH(*p) && len > 0) { + if (MKDIR(npath) == -1) + if (errno != EEXIST) return -1; + } + npath[len++] = *p; + } + + return 0; +} + +static char *strrpl(const char *str, char oldchar, char newchar) { + char *rpl = (char *)malloc(sizeof(char) * (1 + strlen(str))); + char *begin = rpl; + char c; + while((c = *str++)) { + if (c == oldchar) { + c = newchar; + } + *rpl++ = c; + } + *rpl = '\0'; + + return begin; +} + +struct zip_entry_t { + int index; + const char *name; + mz_uint64 uncomp_size; + mz_uint64 comp_size; + mz_uint32 uncomp_crc32; + mz_uint64 offset; + mz_uint8 header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + mz_uint64 header_offset; + mz_uint16 method; + mz_zip_writer_add_state state; + tdefl_compressor comp; +}; + +struct zip_t { + mz_zip_archive archive; + mz_uint level; + struct zip_entry_t entry; + char mode; +}; + +struct zip_t *zip_open(const char *zipname, int level, char mode) { + struct zip_t *zip = NULL; + + if (!zipname || strlen(zipname) < 1) { + // zip_t archive name is empty or NULL + goto cleanup; + } + + if (level < 0) level = MZ_DEFAULT_LEVEL; + if ((level & 0xF) > MZ_UBER_COMPRESSION) { + // Wrong compression level + goto cleanup; + } + + zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t)); + if (!zip) goto cleanup; + + zip->level = level; + zip->mode = mode; + switch (mode) { + case 'w': + // Create a new archive. + if (!mz_zip_writer_init_file(&(zip->archive), zipname, 0)) { + // Cannot initialize zip_archive writer + goto cleanup; + } + break; + + case 'r': + case 'a': + if (!mz_zip_reader_init_file( + &(zip->archive), zipname, + level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) { + // An archive file does not exist or cannot initialize + // zip_archive reader + goto cleanup; + } + + if (mode == 'a' && + !mz_zip_writer_init_from_reader(&(zip->archive), zipname)) { + mz_zip_reader_end(&(zip->archive)); + goto cleanup; + } + + break; + + default: + goto cleanup; + } + + return zip; + +cleanup: + CLEANUP(zip); + return NULL; +} + +void zip_close(struct zip_t *zip) { + if (zip) { + // Always finalize, even if adding failed for some reason, so we have a + // valid central directory. + mz_zip_writer_finalize_archive(&(zip->archive)); + + mz_zip_writer_end(&(zip->archive)); + mz_zip_reader_end(&(zip->archive)); + + CLEANUP(zip); + } +} + +int zip_entry_open(struct zip_t *zip, const char *entryname) { + char *locname = NULL; + size_t entrylen = 0; + mz_zip_archive *pzip = NULL; + mz_uint num_alignment_padding_bytes, level; + + if (!zip || !entryname) { + return -1; + } + + entrylen = strlen(entryname); + if (entrylen < 1) { + return -1; + } + + pzip = &(zip->archive); + /* + .ZIP File Format Specification Version: 6.3.3 + + 4.4.17.1 The name of the file, with optional relative path. + The path stored MUST not contain a drive or + device letter, or a leading slash. All slashes + MUST be forward slashes '/' as opposed to + backwards slashes '\' for compatibility with Amiga + and UNIX file systems etc. If input came from standard + input, there is no file name field. + */ + locname = strrpl(entryname, '\\', '/'); + + if (zip->mode == 'r') { + zip->entry.index = mz_zip_reader_locate_file(pzip, locname, NULL, 0); + CLEANUP(locname); + return (zip->entry.index < 0) ? -1 : 0; + } + + zip->entry.index = zip->archive.m_total_files; + zip->entry.name = locname; + if (!zip->entry.name) { + // Cannot parse zip entry name + return -1; + } + + zip->entry.comp_size = 0; + zip->entry.uncomp_size = 0; + zip->entry.uncomp_crc32 = MZ_CRC32_INIT; + zip->entry.offset = zip->archive.m_archive_size; + zip->entry.header_offset = zip->archive.m_archive_size; + memset(zip->entry.header, 0, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8)); + zip->entry.method = 0; + + num_alignment_padding_bytes = + mz_zip_writer_compute_padding_needed_for_file_alignment(pzip); + + if (!pzip->m_pState || (pzip->m_zip_mode != MZ_ZIP_MODE_WRITING)) { + // Wrong zip mode + return -1; + } + if (zip->level & MZ_ZIP_FLAG_COMPRESSED_DATA) { + // Wrong zip compression level + return -1; + } + // no zip64 support yet + if ((pzip->m_total_files == 0xFFFF) || + ((pzip->m_archive_size + num_alignment_padding_bytes + + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + entrylen) > 0xFFFFFFFF)) { + // No zip64 support yet + return -1; + } + if (!mz_zip_writer_write_zeros( + pzip, zip->entry.offset, + num_alignment_padding_bytes + sizeof(zip->entry.header))) { + // Cannot memset zip entry header + return -1; + } + + zip->entry.header_offset += num_alignment_padding_bytes; + if (pzip->m_file_offset_alignment) { + MZ_ASSERT((zip->entry.header_offset & + (pzip->m_file_offset_alignment - 1)) == 0); + } + zip->entry.offset += + num_alignment_padding_bytes + sizeof(zip->entry.header); + + if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, zip->entry.name, + entrylen) != entrylen) { + // Cannot write data to zip entry + return -1; + } + + zip->entry.offset += entrylen; + level = zip->level & 0xF; + if (level) { + zip->entry.state.m_pZip = pzip; + zip->entry.state.m_cur_archive_file_ofs = zip->entry.offset; + zip->entry.state.m_comp_size = 0; + + if (tdefl_init(&(zip->entry.comp), mz_zip_writer_add_put_buf_callback, + &(zip->entry.state), + tdefl_create_comp_flags_from_zip_params( + level, -15, MZ_DEFAULT_STRATEGY)) != + TDEFL_STATUS_OKAY) { + // Cannot initialize the zip compressor + return -1; + } + } + + return 0; +} + +int zip_entry_close(struct zip_t *zip) { + mz_zip_archive *pzip = NULL; + mz_uint level; + tdefl_status done; + mz_uint16 entrylen; + time_t t; + struct tm *tm; + mz_uint16 dos_time, dos_date; + int status = -1; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + if (zip->mode == 'r') { + return 0; + } + + pzip = &(zip->archive); + level = zip->level & 0xF; + if (level) { + done = tdefl_compress_buffer(&(zip->entry.comp), "", 0, TDEFL_FINISH); + if (done != TDEFL_STATUS_DONE && done != TDEFL_STATUS_OKAY) { + // Cannot flush compressed buffer + goto cleanup; + } + zip->entry.comp_size = zip->entry.state.m_comp_size; + zip->entry.offset = zip->entry.state.m_cur_archive_file_ofs; + zip->entry.method = MZ_DEFLATED; + } + + entrylen = (mz_uint16)strlen(zip->entry.name); + t = time(NULL); + tm = localtime(&t); + dos_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + + ((tm->tm_sec) >> 1)); + dos_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + + ((tm->tm_mon + 1) << 5) + tm->tm_mday); + + // no zip64 support yet + if ((zip->entry.comp_size > 0xFFFFFFFF) || + (zip->entry.offset > 0xFFFFFFFF)) { + // No zip64 support, yet + goto cleanup; + } + + if (!mz_zip_writer_create_local_dir_header( + pzip, zip->entry.header, entrylen, 0, zip->entry.uncomp_size, + zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, 0, + dos_time, dos_date)) { + // Cannot create zip entry header + goto cleanup; + } + + if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.header_offset, + zip->entry.header, sizeof(zip->entry.header)) != + sizeof(zip->entry.header)) { + // Cannot write zip entry header + goto cleanup; + } + + if (!mz_zip_writer_add_to_central_dir( + pzip, zip->entry.name, entrylen, NULL, 0, "", 0, + zip->entry.uncomp_size, zip->entry.comp_size, + zip->entry.uncomp_crc32, zip->entry.method, 0, dos_time, dos_date, + zip->entry.header_offset, 0)) { + // Cannot write to zip central dir + goto cleanup; + } + + pzip->m_total_files++; + pzip->m_archive_size = zip->entry.offset; + status = 0; + +cleanup: + CLEANUP(zip->entry.name); + return status; +} + +int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) { + mz_uint level; + mz_zip_archive *pzip = NULL; + tdefl_status status; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + pzip = &(zip->archive); + if (buf && bufsize > 0) { + zip->entry.uncomp_size += bufsize; + zip->entry.uncomp_crc32 = (mz_uint32)mz_crc32( + zip->entry.uncomp_crc32, (const mz_uint8 *)buf, bufsize); + + level = zip->level & 0xF; + if (!level) { + if ((pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, buf, + bufsize) != bufsize)) { + // Cannot write buffer + return -1; + } + zip->entry.offset += bufsize; + zip->entry.comp_size += bufsize; + } else { + status = tdefl_compress_buffer(&(zip->entry.comp), buf, bufsize, + TDEFL_NO_FLUSH); + if (status != TDEFL_STATUS_DONE && status != TDEFL_STATUS_OKAY) { + // Cannot compress buffer + return -1; + } + } + } + + return 0; +} + +int zip_entry_fwrite(struct zip_t *zip, const char *filename) { + int status = 0; + size_t n = 0; + FILE *stream = NULL; + mz_uint8 buf[MZ_ZIP_MAX_IO_BUF_SIZE] = {0}; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + stream = fopen(filename, "rb"); + if (!stream) { + // Cannot open filename + return -1; + } + + while ((n = fread(buf, sizeof(mz_uint8), MZ_ZIP_MAX_IO_BUF_SIZE, stream)) > + 0) { + if (zip_entry_write(zip, buf, n) < 0) { + status = -1; + break; + } + } + fclose(stream); + + return status; +} + +int zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize) { + mz_zip_archive *pzip = NULL; + mz_uint idx; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + if (zip->mode != 'r' || zip->entry.index < 0) { + // the entry is not found or we do not have read access + return -1; + } + + pzip = &(zip->archive); + idx = (mz_uint)zip->entry.index; + if (mz_zip_reader_is_file_a_directory(pzip, idx)) { + // the entry is a directory + return -1; + } + + *buf = mz_zip_reader_extract_to_heap(pzip, idx, bufsize, 0); + return (*buf) ? 0 : -1; +} + +int zip_entry_fread(struct zip_t *zip, const char *filename) { + mz_zip_archive *pzip = NULL; + mz_uint idx; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + if (zip->mode != 'r' || zip->entry.index < 0) { + // the entry is not found or we do not have read access + return -1; + } + + pzip = &(zip->archive); + idx = (mz_uint)zip->entry.index; + if (mz_zip_reader_is_file_a_directory(pzip, idx)) { + // the entry is a directory + return -1; + } + + return (mz_zip_reader_extract_to_file(pzip, idx, filename, 0)) ? 0 : -1; +} + +int zip_entry_extract(struct zip_t *zip, + size_t (*on_extract)(void *arg, unsigned long long offset, + const void *buf, size_t bufsize), + void *arg) { + mz_zip_archive *pzip = NULL; + mz_uint idx; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + if (zip->mode != 'r' || zip->entry.index < 0) { + // the entry is not found or we do not have read access + return -1; + } + + pzip = &(zip->archive); + idx = (mz_uint)zip->entry.index; + + return (mz_zip_reader_extract_to_callback(pzip, idx, on_extract, arg, 0)) + ? 0 + : -1; +} + +int zip_create(const char *zipname, const char *filenames[], size_t len) { + int status = 0; + size_t i; + mz_zip_archive zip_archive; + + if (!zipname || strlen(zipname) < 1) { + // zip_t archive name is empty or NULL + return -1; + } + + // Create a new archive. + if (!memset(&(zip_archive), 0, sizeof(zip_archive))) { + // Cannot memset zip archive + return -1; + } + + if (!mz_zip_writer_init_file(&zip_archive, zipname, 0)) { + // Cannot initialize zip_archive writer + return -1; + } + + for (i = 0; i < len; ++i) { + const char *name = filenames[i]; + if (!name) { + status = -1; + break; + } + + if (!mz_zip_writer_add_file(&zip_archive, basename(name), name, "", 0, + ZIP_DEFAULT_COMPRESSION_LEVEL)) { + // Cannot add file to zip_archive + status = -1; + break; + } + } + + mz_zip_writer_finalize_archive(&zip_archive); + mz_zip_writer_end(&zip_archive); + return status; +} + +int zip_extract(const char *zipname, const char *dir, + int (*on_extract)(const char *filename, void *arg), void *arg) { + int status = -1; + mz_uint i, n; + char path[MAX_PATH + 1] = {0}; + mz_zip_archive zip_archive; + mz_zip_archive_file_stat info; + size_t dirlen = 0; + + if (!memset(&(zip_archive), 0, sizeof(zip_archive))) { + // Cannot memset zip archive + return -1; + } + + if (!zipname || !dir) { + // Cannot parse zip archive name + return -1; + } + + dirlen = strlen(dir); + if (dirlen + 1 > MAX_PATH) { + return -1; + } + + // Now try to open the archive. + if (!mz_zip_reader_init_file(&zip_archive, zipname, 0)) { + // Cannot initialize zip_archive reader + return -1; + } + + strcpy(path, dir); + if (!ISSLASH(path[dirlen - 1])) { +#if defined _WIN32 || defined __WIN32__ + path[dirlen] = '\\'; +#else + path[dirlen] = '/'; +#endif + ++dirlen; + } + + // Get and print information about each file in the archive. + n = mz_zip_reader_get_num_files(&zip_archive); + for (i = 0; i < n; ++i) { + if (!mz_zip_reader_file_stat(&zip_archive, i, &info)) { + // Cannot get information about zip archive; + goto out; + } + strncpy(&path[dirlen], info.m_filename, MAX_PATH - dirlen); + if (mkpath(path) < 0) { + // Cannot make a path + goto out; + } + + if (!mz_zip_reader_is_file_a_directory(&zip_archive, i)) { + if (!mz_zip_reader_extract_to_file(&zip_archive, i, path, 0)) { + // Cannot extract zip archive to file + goto out; + } + } + + if (on_extract) { + if (on_extract(path, arg) < 0) { + goto out; + } + } + } + status = 0; + +out: + // Close the archive, freeing any resources it was using + if (!mz_zip_reader_end(&zip_archive)) { + // Cannot end zip reader + status = -1; + } + + return status; +} diff --git a/contrib/zip/src/zip.h b/contrib/zip/src/zip.h new file mode 100644 index 000000000..1611b5417 --- /dev/null +++ b/contrib/zip/src/zip.h @@ -0,0 +1,193 @@ +/* + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once +#ifndef ZIP_H +#define ZIP_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef MAX_PATH +#define MAX_PATH 32767 /* # chars in a path name including NULL */ +#endif + +#define ZIP_DEFAULT_COMPRESSION_LEVEL 6 + +/* + This data structure is used throughout the library to represent zip archive + - forward declaration. +*/ +struct zip_t; + +/* + Opens zip archive with compression level using the given mode. + + Args: + zipname: zip archive file name. + level: compression level (0-9 are the standard zlib-style levels). + mode: file access mode. + 'r': opens a file for reading/extracting (the file must exists). + 'w': creates an empty file for writing. + 'a': appends to an existing archive. + + Returns: + The zip archive handler or NULL on error +*/ +extern struct zip_t *zip_open(const char *zipname, int level, char mode); + +/* + Closes zip archive, releases resources - always finalize. + + Args: + zip: zip archive handler. +*/ +extern void zip_close(struct zip_t *zip); + +/* + Opens a new entry for writing in a zip archive. + + Args: + zip: zip archive handler. + entryname: an entry name in local dictionary. + + Returns: + The return code - 0 on success, negative number (< 0) on error. +*/ +extern int zip_entry_open(struct zip_t *zip, const char *entryname); + +/* + Closes a zip entry, flushes buffer and releases resources. + + Args: + zip: zip archive handler. + + Returns: + The return code - 0 on success, negative number (< 0) on error. +*/ +extern int zip_entry_close(struct zip_t *zip); + +/* + Compresses an input buffer for the current zip entry. + + Args: + zip: zip archive handler. + buf: input buffer. + bufsize: input buffer size (in bytes). + + Returns: + The return code - 0 on success, negative number (< 0) on error. +*/ +extern int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize); + +/* + Compresses a file for the current zip entry. + + Args: + zip: zip archive handler. + filename: input file. + + Returns: + The return code - 0 on success, negative number (< 0) on error. +*/ +extern int zip_entry_fwrite(struct zip_t *zip, const char *filename); + +/* + Extracts the current zip entry into output buffer. + The function allocates sufficient memory for a output buffer. + + Args: + zip: zip archive handler. + buf: output buffer. + bufsize: output buffer size (in bytes). + + Note: + - remember to release memory allocated for a output buffer. + - for large entries, please take a look at zip_entry_extract function. + + Returns: + The return code - 0 on success, negative number (< 0) on error. +*/ +extern int zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize); + +/* + Extracts the current zip entry into output file. + + Args: + zip: zip archive handler. + filename: output file. + + Returns: + The return code - 0 on success, negative number (< 0) on error. +*/ +extern int zip_entry_fread(struct zip_t *zip, const char *filename); + +/* + Extract the current zip entry using a callback function (on_extract). + + Args: + zip: zip archive handler. + on_extract: callback function. + arg: opaque pointer (optional argument, + which you can pass to the on_extract callback) + + Returns: + The return code - 0 on success, negative number (< 0) on error. + */ +extern int zip_entry_extract(struct zip_t *zip, + size_t (*on_extract)(void *arg, + unsigned long long offset, + const void *data, + size_t size), + void *arg); + +/* + Creates a new archive and puts files into a single zip archive. + + Args: + zipname: zip archive file. + filenames: input files. + len: number of input files. + + Returns: + The return code - 0 on success, negative number (< 0) on error. +*/ +extern int zip_create(const char *zipname, const char *filenames[], size_t len); + +/* + Extracts a zip archive file into directory. + + If on_extract_entry is not NULL, the callback will be called after + successfully extracted each zip entry. + Returning a negative value from the callback will cause abort and return an + error. The last argument (void *arg) is optional, which you can use to pass + data to the on_extract_entry callback. + + Args: + zipname: zip archive file. + dir: output directory. + on_extract_entry: on extract callback. + arg: opaque pointer. + + Returns: + The return code - 0 on success, negative number (< 0) on error. +*/ +extern int zip_extract(const char *zipname, const char *dir, + int (*on_extract_entry)(const char *filename, void *arg), + void *arg); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/contrib/zip/test/CMakeLists.txt b/contrib/zip/test/CMakeLists.txt new file mode 100644 index 000000000..7734dcbe7 --- /dev/null +++ b/contrib/zip/test/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 2.8) + +# test +include_directories(../src) +add_executable(test.exe test.c ../src/zip.c) + +add_test(NAME test COMMAND test.exe) diff --git a/contrib/zip/test/test.c b/contrib/zip/test/test.c new file mode 100644 index 000000000..0b9c9f7b6 --- /dev/null +++ b/contrib/zip/test/test.c @@ -0,0 +1,105 @@ +#include + +#include +#include +#include +#include + +#define ZIPNAME "test.zip" +#define TESTDATA1 "Some test data 1...\0" +#define TESTDATA2 "Some test data 2...\0" + +static void test_write(void) { + struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w'); + assert(zip != NULL); + + assert(0 == zip_entry_open(zip, "test/test-1.txt")); + assert(0 == zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1))); + assert(0 == zip_entry_close(zip)); + + zip_close(zip); +} + +static void test_append(void) { + struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'a'); + assert(zip != NULL); + + assert(0 == zip_entry_open(zip, "test\\test-2.txt")); + assert(0 == zip_entry_write(zip, TESTDATA2, strlen(TESTDATA2))); + assert(0 == zip_entry_close(zip)); + + zip_close(zip); +} + +static void test_read(void) { + char *buf = NULL; + size_t bufsize; + struct zip_t *zip = zip_open(ZIPNAME, 0, 'r'); + assert(zip != NULL); + + assert(0 == zip_entry_open(zip, "test\\test-1.txt")); + assert(0 == zip_entry_read(zip, (void **)&buf, &bufsize)); + assert(bufsize == strlen(TESTDATA1)); + assert(0 == strncmp(buf, TESTDATA1, bufsize)); + assert(0 == zip_entry_close(zip)); + free(buf); + buf = NULL; + bufsize = 0; + + assert(0 == zip_entry_open(zip, "test/test-2.txt")); + assert(0 == zip_entry_read(zip, (void **)&buf, &bufsize)); + assert(bufsize == strlen(TESTDATA2)); + assert(0 == strncmp(buf, TESTDATA2, bufsize)); + assert(0 == zip_entry_close(zip)); + free(buf); + buf = NULL; + bufsize = 0; + + zip_close(zip); +} + +struct buffer_t { + char *data; + size_t size; +}; + +static size_t on_extract(void *arg, unsigned long long offset, const void *data, + size_t size) { + struct buffer_t *buf = (struct buffer_t *)arg; + buf->data = realloc(buf->data, buf->size + size + 1); + assert(NULL != buf->data); + + memcpy(&(buf->data[buf->size]), data, size); + buf->size += size; + buf->data[buf->size] = 0; + + return size; +} + +static void test_extract(void) { + struct buffer_t buf = {0}; + + struct zip_t *zip = zip_open(ZIPNAME, 0, 'r'); + assert(zip != NULL); + + assert(0 == zip_entry_open(zip, "test/test-1.txt")); + assert(0 == zip_entry_extract(zip, on_extract, &buf)); + + assert(buf.size == strlen(TESTDATA1)); + assert(0 == strncmp(buf.data, TESTDATA1, buf.size)); + assert(0 == zip_entry_close(zip)); + free(buf.data); + buf.data = NULL; + buf.size = 0; + + zip_close(zip); +} + +int main(int argc, char *argv[]) { + test_write(); + test_append(); + test_read(); + test_extract(); + + return remove(ZIPNAME); +} diff --git a/contrib/zip/zip.png b/contrib/zip/zip.png new file mode 100644 index 0000000000000000000000000000000000000000..244681007ce84500da511872192f176ccbe0edc3 GIT binary patch literal 5275 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 Date: Mon, 27 Nov 2017 22:46:57 +0100 Subject: [PATCH 380/490] fix unittest. --- code/D3MFExporter.cpp | 89 +++++++++++++++++++------------- code/D3MFExporter.h | 4 +- test/unit/utD3MFImportExport.cpp | 2 +- 3 files changed, 56 insertions(+), 39 deletions(-) diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index 524f54400..462c30c76 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -104,46 +104,54 @@ bool D3MFExporter::exportArchive( const char *file ) { if ( nullptr == m_zipArchive ) { return false; } - ok |= exportRelations(); ok |= export3DModel(); + ok |= exportRelations(); + + zip_close( m_zipArchive ); + m_zipArchive = nullptr; return ok; } bool D3MFExporter::exportRelations() { - mOutput.clear(); + mRelOutput.clear(); - mOutput << "\n"; - mOutput << "\n"; + mRelOutput << "\n"; + mRelOutput << "\n"; for ( size_t i = 0; i < mRelations.size(); ++i ) { - mOutput << "target << " "; - mOutput << "id=\"" << mRelations[i]->id << " "; - mOutput << "Type=\"" << mRelations[ i ]->type << "/>\n"; + mRelOutput << "target << "\" "; + mRelOutput << "id=\"" << mRelations[i]->id << "\" "; + mRelOutput << "Type=\"" << mRelations[ i ]->type << "/>"; + mRelOutput << std::endl; } - mOutput << "\n"; + mRelOutput << ""; + mRelOutput << std::endl; writeRelInfoToFile( "_rels", ".rels" ); + mRelOutput.flush(); return true; } bool D3MFExporter::export3DModel() { - mOutput.clear(); + mModelOutput.clear(); writeHeader(); - mOutput << "<" << XmlTag::model << " " << XmlTag::model_unit << "=\"millimeter\"" + mModelOutput << "<" << XmlTag::model << " " << XmlTag::model_unit << "=\"millimeter\"" << "xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\">" - << "\n"; - mOutput << "<" << XmlTag::resources << ">\n"; + << std::endl; + mModelOutput << "<" << XmlTag::resources << ">"; + mModelOutput << std::endl; writeObjects(); - mOutput << "\n"; + mModelOutput << ""; + mModelOutput << std::endl; writeBuild(); - mOutput << "\n"; + mModelOutput << "\n"; OpcPackageRelationship *info = new OpcPackageRelationship; info->id = mArchiveName; @@ -151,15 +159,15 @@ bool D3MFExporter::export3DModel() { info->type = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel"; mRelations.push_back( info ); - writeModelToArchive( "3D", mArchiveName ); - - mOutput.clear(); + writeModelToArchive( "3D", "3DModel.model" ); + mModelOutput.flush(); return true; } void D3MFExporter::writeHeader() { - mOutput << "" << "\n"; + mModelOutput << ""; + mModelOutput << std::endl; } void D3MFExporter::writeObjects() { @@ -173,7 +181,8 @@ void D3MFExporter::writeObjects() { if ( nullptr == currentNode ) { continue; } - mOutput << "<" << XmlTag::object << " id=\"" << currentNode->mName.C_Str() << "\" type=\"model\">\n"; + mModelOutput << "<" << XmlTag::object << " id=\"" << currentNode->mName.C_Str() << "\" type=\"model\">"; + mModelOutput << std::endl; for ( unsigned int j = 0; j < currentNode->mNumMeshes; ++j ) { aiMesh *currentMesh = mScene->mMeshes[ currentNode->mMeshes[ j ] ]; if ( nullptr == currentMesh ) { @@ -183,7 +192,8 @@ void D3MFExporter::writeObjects() { } mBuildItems.push_back( i ); - mOutput << "\n"; + mModelOutput << ""; + mModelOutput << std::endl; } } @@ -192,19 +202,20 @@ void D3MFExporter::writeMesh( aiMesh *mesh ) { return; } - mOutput << "<" << XmlTag::mesh << ">\n"; - mOutput << "<" << XmlTag::vertices << ">\n"; + mModelOutput << "<" << XmlTag::mesh << ">" << std::endl; + mModelOutput << "<" << XmlTag::vertices << ">" << std::endl; for ( unsigned int i = 0; i < mesh->mNumVertices; ++i ) { writeVertex( mesh->mVertices[ i ] ); } - mOutput << "\n"; - mOutput << "\n"; + mModelOutput << "" << std::endl; + mModelOutput << "" << std::endl; writeFaces( mesh ); } void D3MFExporter::writeVertex( const aiVector3D &pos ) { - mOutput << "<" << XmlTag::vertex << " x=\"" << pos.x << "\" y=\"" << pos.y << "\" z=\"" << pos.z << "\">\n"; + mModelOutput << "<" << XmlTag::vertex << " x=\"" << pos.x << "\" y=\"" << pos.y << "\" z=\"" << pos.z << "\">"; + mModelOutput << std::endl; } void D3MFExporter::writeFaces( aiMesh *mesh ) { @@ -215,39 +226,45 @@ void D3MFExporter::writeFaces( aiMesh *mesh ) { if ( !mesh->HasFaces() ) { return; } - mOutput << "<" << XmlTag::triangles << ">\n"; + mModelOutput << "<" << XmlTag::triangles << ">" << std::endl; for ( unsigned int i = 0; i < mesh->mNumFaces; ++i ) { aiFace ¤tFace = mesh->mFaces[ i ]; - mOutput << "<" << XmlTag::triangle << " v1=\"" << currentFace.mIndices[ 0 ] << "\" v2=\"" - << currentFace.mIndices[ 1 ] << "\" v3=\"" << currentFace.mIndices[ 2 ] << "\"/>\n"; + mModelOutput << "<" << XmlTag::triangle << " v1=\"" << currentFace.mIndices[ 0 ] << "\" v2=\"" + << currentFace.mIndices[ 1 ] << "\" v3=\"" << currentFace.mIndices[ 2 ] << "\"/>"; + mModelOutput << std::endl; } - mOutput << "\n"; + mModelOutput << ""; + mModelOutput << std::endl; } void D3MFExporter::writeBuild() { - mOutput << "<" << XmlTag::build << ">\n"; + mModelOutput << "<" << XmlTag::build << ">" << std::endl; for ( size_t i = 0; i < mBuildItems.size(); ++i ) { - mOutput << "<" << XmlTag::item << " objectid=\"" << i + 1 << "\"/>\n"; + mModelOutput << "<" << XmlTag::item << " objectid=\"" << i + 1 << "\"/>"; + mModelOutput << std::endl; } - mOutput << "\n"; + mModelOutput << ""; + mModelOutput << std::endl; } void D3MFExporter::writeModelToArchive( const std::string &folder, const std::string &modelName ) { - const std::string entry = folder + "/" + mArchiveName; + const std::string entry = folder + "/" + modelName; zip_entry_open( m_zipArchive, entry.c_str() ); - const std::string &exportTxt( mOutput.str() ); + const std::string &exportTxt( mModelOutput.str() ); zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() ); zip_entry_close( m_zipArchive ); } void D3MFExporter::writeRelInfoToFile( const std::string &folder, const std::string &relName ) { - const std::string entry = folder + "/" + "_rels"; + const std::string entry = folder + "/" + relName; zip_entry_open( m_zipArchive, entry.c_str() ); - const std::string &exportTxt( mOutput.str() ); + + const std::string &exportTxt( mRelOutput.str() ); zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() ); + zip_entry_close( m_zipArchive ); } diff --git a/code/D3MFExporter.h b/code/D3MFExporter.h index 9a80844ef..7227f957a 100644 --- a/code/D3MFExporter.h +++ b/code/D3MFExporter.h @@ -81,14 +81,14 @@ protected: void writeBuild(); void writeModelToArchive( const std::string &folder, const std::string &modelName ); void writeRelInfoToFile( const std::string &folder, const std::string &relName ); - void createZipArchiveFromeFileStructure( const char* pFile ); private: IOSystem *mIOSystem; std::string mArchiveName; zip_t *m_zipArchive; const aiScene *mScene; - std::ostringstream mOutput; + std::ostringstream mModelOutput; + std::ostringstream mRelOutput; std::vector mBuildItems; std::vector mRelations; }; diff --git a/test/unit/utD3MFImportExport.cpp b/test/unit/utD3MFImportExport.cpp index 0383a27b0..2479dfd19 100644 --- a/test/unit/utD3MFImportExport.cpp +++ b/test/unit/utD3MFImportExport.cpp @@ -67,7 +67,7 @@ public: const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/3MF/box.3mf", 0 ); Assimp::Exporter exporter; - return nullptr != exporter.ExportToBlob( scene, "3mf", 0 ); + return AI_SUCCESS == exporter.Export( scene, "3mf", "test.3mf" ); } }; From 09f7769820f5a8204c14645308a571b16f00d702 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 27 Nov 2017 23:36:32 +0100 Subject: [PATCH 381/490] remove unused attribute. --- code/D3MFExporter.cpp | 7 +++---- code/D3MFExporter.h | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index 462c30c76..ffa51a220 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -55,7 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { void ExportScene3MF( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/ ) { - D3MF::D3MFExporter myExporter( pFile, pIOSystem, pScene ); + D3MF::D3MFExporter myExporter( pFile, pScene ); if ( myExporter.validate() ) { bool ok = myExporter.exportArchive(pFile); if ( !ok ) { @@ -68,9 +68,8 @@ namespace D3MF { #ifndef ASSIMP_BUILD_NO3MF_EXPORTER -D3MFExporter::D3MFExporter( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene ) -: mIOSystem( pIOSystem ) -, mArchiveName( pFile ) +D3MFExporter::D3MFExporter( const char* pFile, const aiScene* pScene ) +: mArchiveName( pFile ) , m_zipArchive( nullptr ) , mScene( pScene ) , mBuildItems() diff --git a/code/D3MFExporter.h b/code/D3MFExporter.h index 7227f957a..fd7d0b683 100644 --- a/code/D3MFExporter.h +++ b/code/D3MFExporter.h @@ -65,7 +65,7 @@ struct OpcPackageRelationship; class D3MFExporter { public: - D3MFExporter( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene ); + D3MFExporter( const char* pFile, const aiScene* pScene ); ~D3MFExporter(); bool validate(); bool exportArchive( const char *file ); @@ -83,7 +83,6 @@ protected: void writeRelInfoToFile( const std::string &folder, const std::string &relName ); private: - IOSystem *mIOSystem; std::string mArchiveName; zip_t *m_zipArchive; const aiScene *mScene; From 4f972661e817462624013743972e586d974ed0db Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 28 Nov 2017 09:08:16 +0100 Subject: [PATCH 382/490] Update D3MFExporter.cpp Fix review findings. --- code/D3MFExporter.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index 462c30c76..71d5e8256 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -68,9 +68,8 @@ namespace D3MF { #ifndef ASSIMP_BUILD_NO3MF_EXPORTER -D3MFExporter::D3MFExporter( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene ) -: mIOSystem( pIOSystem ) -, mArchiveName( pFile ) +D3MFExporter::D3MFExporter( const char* pFile, const aiScene* pScene ) +: mArchiveName( pFile ) , m_zipArchive( nullptr ) , mScene( pScene ) , mBuildItems() @@ -249,6 +248,9 @@ void D3MFExporter::writeBuild() { } void D3MFExporter::writeModelToArchive( const std::string &folder, const std::string &modelName ) { + if ( nullptr == m_zipArchive ) { + throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." ); + } const std::string entry = folder + "/" + modelName; zip_entry_open( m_zipArchive, entry.c_str() ); @@ -259,6 +261,9 @@ void D3MFExporter::writeModelToArchive( const std::string &folder, const std::st } void D3MFExporter::writeRelInfoToFile( const std::string &folder, const std::string &relName ) { + if ( nullptr == m_zipArchive ) { + throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." ); + } const std::string entry = folder + "/" + relName; zip_entry_open( m_zipArchive, entry.c_str() ); From 4b6e49ca7b59d2a7021abb95009aaf5e8f52176a Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 28 Nov 2017 09:09:04 +0100 Subject: [PATCH 383/490] Update D3MFExporter.h Fix review findings. --- code/D3MFExporter.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/code/D3MFExporter.h b/code/D3MFExporter.h index 7227f957a..862e55fbd 100644 --- a/code/D3MFExporter.h +++ b/code/D3MFExporter.h @@ -55,7 +55,6 @@ struct zip_t; namespace Assimp { class IOStream; -class IOSystem; namespace D3MF { @@ -65,7 +64,7 @@ struct OpcPackageRelationship; class D3MFExporter { public: - D3MFExporter( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene ); + D3MFExporter( const char* pFile, const aiScene* pScene ); ~D3MFExporter(); bool validate(); bool exportArchive( const char *file ); @@ -83,7 +82,6 @@ protected: void writeRelInfoToFile( const std::string &folder, const std::string &relName ); private: - IOSystem *mIOSystem; std::string mArchiveName; zip_t *m_zipArchive; const aiScene *mScene; From 6a2cd1c91d7f4a09ad048a6cb2545e6db0112861 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 28 Nov 2017 10:14:23 +0100 Subject: [PATCH 384/490] Update D3MFExporter.cpp Fix the build --- code/D3MFExporter.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index 71d5e8256..16889720a 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -55,7 +55,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { void ExportScene3MF( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/ ) { - D3MF::D3MFExporter myExporter( pFile, pIOSystem, pScene ); + if ( nullptr == pIOSystem ) { + throw DeadlyExportError( "Could not export 3MP archive: " + std::string( pFile ) ); + } + D3MF::D3MFExporter myExporter( pFile, pScene ); if ( myExporter.validate() ) { bool ok = myExporter.exportArchive(pFile); if ( !ok ) { From ef4842e128d8011dfeefdaa5b9d9e1939d8ef83e Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 28 Nov 2017 15:15:43 +0100 Subject: [PATCH 385/490] Update .travis.sh Make check more verbose. --- .travis.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.sh b/.travis.sh index fb42bd40d..397e75077 100755 --- a/.travis.sh +++ b/.travis.sh @@ -46,7 +46,7 @@ if [ "$TRAVIS_OS_NAME" = "linux" ]; then if [ $ANALYZE = "ON" ] ; then if [ "$CC" = "clang" ]; then scan-build cmake -G "Unix Makefiles" -DBUILD_SHARED_LIBS=OFF -DASSIMP_BUILD_TESTS=OFF - scan-build --status-bugs make -j2 + scan-build --status-bugs make -j2 -v else cppcheck --version generate \ From b5e79c3cb620afa6244b8d14607a9a20471a7e65 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 29 Nov 2017 16:11:33 +0100 Subject: [PATCH 386/490] fix invalid include --- code/D3MFExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index a825dcb54..74fa7a6b4 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -42,7 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include +#include #include #include From fd2da59427d5ed9bb2e430fc76a3b165d45f165f Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 29 Nov 2017 16:23:12 +0100 Subject: [PATCH 387/490] fix unaligned memory access. --- contrib/zip/src/zip.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/zip/src/zip.c b/contrib/zip/src/zip.c index 80573096b..6a5dbcbf0 100644 --- a/contrib/zip/src/zip.c +++ b/contrib/zip/src/zip.c @@ -8,6 +8,8 @@ * OTHER DEALINGS IN THE SOFTWARE. */ +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 + #include "zip.h" #include "miniz.h" From 59dcfefeb2cb47cfe00308f831e328accc215401 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 29 Nov 2017 16:45:09 +0100 Subject: [PATCH 388/490] fix unaligned memory access. --- contrib/zip/src/miniz.h | 2 +- contrib/zip/src/zip.c | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/contrib/zip/src/miniz.h b/contrib/zip/src/miniz.h index da86f6e8f..935ee3d5f 100644 --- a/contrib/zip/src/miniz.h +++ b/contrib/zip/src/miniz.h @@ -212,7 +212,7 @@ #if MINIZ_X86_OR_X64_CPU // Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. -#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 #endif #if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) diff --git a/contrib/zip/src/zip.c b/contrib/zip/src/zip.c index 6a5dbcbf0..80573096b 100644 --- a/contrib/zip/src/zip.c +++ b/contrib/zip/src/zip.c @@ -8,8 +8,6 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 - #include "zip.h" #include "miniz.h" From 67c236647d6f285b0c34163694c94776c8439fb5 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Wed, 29 Nov 2017 11:20:09 -0500 Subject: [PATCH 389/490] Restore import of multi mesh binary STLs Regression introduced in: 9a9f18bbed8938323548335d9225e480a0ea8ca6 This restores the behaviour for binary STLs with multiple bodies. The code could be improved (to reuse code found at the end of LoadASCIIFile) but for now this is a quick fix --- code/STLLoader.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index ab2dcf36e..96a81c22f 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -505,6 +505,12 @@ bool STLImporter::LoadBinaryFile() // now copy faces addFacesToMesh(pMesh); + // add all created meshes to the single node + pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; + pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; + for (unsigned int i = 0; i < pScene->mNumMeshes; i++) + pScene->mRootNode->mMeshes[i] = i; + if (bIsMaterialise && !pMesh->mColors[0]) { // use the color as diffuse material color From 5c9ed540b2c9a1e892adf6472bb1aa5b3a65c013 Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Wed, 29 Nov 2017 12:15:15 -0500 Subject: [PATCH 390/490] Add test for GLB import Currently fails (testing the issue in #1600) --- .../2CylinderEngine.glb | Bin 0 -> 1838084 bytes test/unit/utglTF2ImportExport.cpp | 10 ++++++++++ 2 files changed, 10 insertions(+) create mode 100644 test/models/glTF2/2CylinderEngine-glTF-Binary/2CylinderEngine.glb diff --git a/test/models/glTF2/2CylinderEngine-glTF-Binary/2CylinderEngine.glb b/test/models/glTF2/2CylinderEngine-glTF-Binary/2CylinderEngine.glb new file mode 100644 index 0000000000000000000000000000000000000000..0e9b10568c74ce93743161c76dd6d8c1428bd48b GIT binary patch literal 1838084 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 Xvk3cVhYBLIhRmc}1in z!PFd*#22b^d*Noe!Bffp?Z`^^No23KkAl)dcS=tU!YrRFsf(Cij&j{AoWA$C!YW%6Os$5 zGLs;Mg3`pjV}Py;*t7lFY=Tk&BxCn~ew6K_lpoar^u`V{E5I{7|3P;NlIiDkM#=mJ z=0v&%rAEnI>F?kZBwHvfwKqQTQbD`TCn&4@1E&Or-j}?0p&~mu+n>xQcsc`xfZ28{ z_*BkMkMRbu)x3W| z)S;>hceD47LL~Vta)FUR*AA$BjdGK~V>aU=O(C$F`csSxR7yZB?(b4j%Y^(XiXd^z z95+K?8?fxdm2s45(kAG>2VS@q$_q9zy*L6}08~xy>ft0kxZQ|SRw@Zjn3i01U)9s3 z(y6`jMRP7?x3+WYLSr5#_klgmN{RW}vsCI^L~G9^`tcT-E{)5L`!6yowt zDLNWFpbI|*dJ^9+ z;Zvh92={+*KK89IDv3fJKD|*!)nxRnbaLrb_FQoQI0m?d2C_m`72PM{64I(2FZ+nu zc9A+>R5{ofM-g*!K~+Cp+!dqH?%wDh$gtA9$$XqeCd5>~sGYhieM- zyhQ4<6+$b}lVCNRCO^mtKLPKdqr4eMgB$B~?13cmP)G>>9A^#hk6vp)O)9IEhrGIcWtsLJW0 znr9!U3o8J-wrnP6iu|g6x{xk-NF@s?r6e;hY1(tyyo>+KEjQ2c>Hw5g`S6!hBNqyyWlyPLC{97z`;$mWTuID=v=kF@@IP@teOh~e4=w-^k?9@;vu`f|6=SOA$b|NnBLbT!9079JL zDBQUQ{&8svCO*6}{Gyy-E<`(Vnu!<;-|>JOh37bM0|1Te{ej!)o-(r^-?7G75+hXQ z(4da8ZzFYRLB1@XQN=jiDGp*^m7NWNBGAMtzVnsx5tT%a5sg2Ym$BeAdMO6i3_-Wcor34CW zG~g8sdh>)rq*c~?xDG@Y=d5sGp zs)+cEt0-TQU#I)gS#X;=$~5nsweIh%A;aCOV8TLoQd%(qa_IeJQ?|KMtcK*8J(tC0e;TL3w#S3j`#nAM z8wyo$$rP9J`K3*~8BDc)WWm6L#!8a~KX6UY;^G_h129wyh}VzkT>&sA87??8 z>>pMOR3Q0;@DLKIVDiDLV71WC1(T7mHzyB=no>S^x$qAh8a>vw53C_}k))76{nU^I z$}U4vM3XjuWex(pxIsS|Ll;S5isDH`yyFh_MZdI@N_OY$pSNIn7*#nnJnfu4@{y8- z$}ez_H;qf_D|ypB@T!n3{jT)LJ%uWQCXaSEUi};dL^;uZyQE zVKuWztIA2@p$ z%OX%IREf8MY;l?_v$W0>DTN9W7&#skB_gNV}nVR1?ijb`s8*db7_D=M35m@h?~b z&|vt8*dahK~)-Fp`vQp)IyOy;8%c9)(OQ)k1lbdh##uLK_Z_C znMiq{Dw-|G^x{RhY`YMLhI&$TNucqM9U8s=voGKHuRJvRK?7vEvhDZpbZRJ^;;Sut zvs_>!(%qoMsnIJBNn#QtvVXBnDtX;^I)+~H`HOgse!+l~rkNW~9UDI*WG#T-2g7fs zP|+=UX!rw`;gEDnK8OZYWNY~ytr zWM1h3#)R)|o0GWXL%;ch2X0U_j%VrM%OROZHT?93EJCd)RxIBg@h5n|vz-n6Vm^ee8?*Q>#G;68SegDC_2* zQpGDk^jsZI5;i-MEr|M2%}BZnPA?_VSHOSt+otrsDqc4S_ad!Byvo0bKpAp)g7kKu z^&7?5Z512<5E6#VhSd}w(!;Ix8R9ALBs#0E#1Jt@bT!*-DTxK^%$^LdK7qnv;~^zU z<>LI-a7GiJY(g4_e!hj?E~Z~MNJYc(8T~9Q0)CMDe9Z&l;4H&d-C;ll7ya(!e(z;J zDS*!t4@-kAti*c~-T37`)C}TRaqQm-f|g{0fW8EK<61<*Cbg0jX+%JS48|>5ysMPB zDLZ~h(Yg7>gU5_17}{;zq!A;>;ubBv)ih-A#QZbIjTu+ee(=!Zad;7`AEmn}#DiB! z@vbFN8OU^hn!tTDtu>219$0OLqiKF&!NiGpF3N7jEXg+%@g?iog>$VX4|H2^Jyc=Q z$>Hpn+n=%y>ye8ptRoAiKsRI!L0sdyCC-`H{?A;c{b0;LZk2SV^l~=-;O-EfW<&Os zgl1;jpGkYx%LD&n^H1TRe=0V!@OeT6{fVULvYv#g0x8lOvdEi^)&7aAbX!k@RB@HG z(!X+9_N!KZ|7B%hDGTDVH2s-JTy=f?w_J3Bga2Aw{-fzKi|F}Zn>v3FiTJl=P!mY116!v8(L;a|~|3Od-NL@|UvF&yw$GR*JLXRxe8v>|9;WGBV3qe(SHxqB_SvOM1ep-2FTphGlTMJ#f)ba z5sOA(7>N+N$l57??1H{AVmxbsVgd9^Sh#pLqyLN(Pdw2(c{p~_wI7Bj-#SI@{N{M`=bf#M*Iu2bK0PsLR>Pm>FOO5Y#R`VYcQ#aye$quz z`_41p32>+J%w(;)je6{+eQlm$(oAJ}NMYP`VCy#$( z)PG{A%3U5-H+^%Ora2zi(MVl)*0~J#ncYsc`}SBp^4gBZQ&$yYzGY_Z&kghar-mxW z>$A;uuW0keo%yQ&vKP3$2i00) zzTcp=(c!_d`i6;h%oXY-qf?I&`mABI)Z8vx+Z@sK!q}tTbxbUd_Kwe75q7=$V{FvOSqk_J6He*u=-lZ1-Wonj^}Mrpc$UW%yZI*cU-nj* z&hqT*7lcoKVUqgx>^UakwZo_R>bgO1o3o9#!VPa3r5uabn5{OPWSlhlEOq7sjdX60 zdWPzLzVdB6P(L`(Y1H~QtZv+LGUM*$6Hiu`9TnCe=bvk|d;J8pylq53TAXiuv$UG} zc}-7-ySLe6Ot`<7b_||jeDKX}M#B!H7{0Id0;Ar0WAwYXU24$W1V@Fx}eoTxX6YGhI^~A)QO|h5QDMys3uP65= zxnqj`nzc?ZQIHwNo4d|6x7=Q;h)*s*tC88bNr@tQTIw2V3fXH~bP@)g*z`kkI1L63 zuzVsK_&dVKwVKQ{ykhWd^Iy1HwY}j%>S93~hM*rq(3l};%n&qb^Ow)P#6h(6Fjq2mgZwgBk z@#5NFFEwW#Gllien`0)KH5N}Mc@^$n^FlM+qJ(vgYvvsDgISYFF16XMFE-nZDPj1I zWy$tc)c#qI5|(2lZk%Jz9Y0xJv3>QbO;22GzS6Zs&D=68woG4UHl9D3#*DSuIm@0m zM)UTa$!m*^du^2205So#O03S&7n^sjpTaiD0g*xWzGAd5YriJ#SIA^@t*=Ban|+#j z#h3j}Vdt2yPcBt2VHyEsp|e|d(t7XrdO7#qdIlscESVUfcg`z z7*CM<4KFHHzMHz5gfC0(nIsV`xyP{R(t9x+R8 zJ8Vvj&dE7zhI(Pi!6w-dN5jT=t^S-C^%3g~-!f*FYTx3*80Kvw_7WC*3=@rkzG6gk z0uQ#nApC?s9|b+qa88WYEY`D1tRW`mS|!%AO00R6(6hFjB;9ISwrdQwt7*}H4D@db zJsbPD0ozEi3x^$SP6kez%CfzcIlthTs=N@QWe%#Sr{r2!1gHzZimF z48bpk;1|1{_{9+XVhDaQraV5GY`@r$1{WHFckFqIUkt%7hTs=N@Qd+or>V?G(HpuL zf?o{5BZlA$7xOxPGPVZ+3P z-C_#6#Xet>Z|0l{C2G-*j%<_n>|COD4j4+dswvhPYYVwIW&0G9>_xHW*bxIt)F&-2 zruD}J%@|F0O(z)`o6!4UL(mD!Qj5-Qo0)7swqsy3lTFBWD{N-66%AoSDq&kHVSg%N ze=4$*EgMyl9c$UH3|ls>5_Ye8ar>nPcrNxzPsreu&8!-qFx!Cq2w!}Ei4yj+y6x48 zMwdBjjmLk0Pm{8}d0xvVXV|jUm9XIz`9+p(uY}ES?~D8bwsDrNYOjWUava&ew(nr$ zhkOddvYE-(U|Wsk!QauY`FG8UllwFAApDw`@M~CSBxc(`TUf{hL&yX}$N@v}epu+- zu&_~$e_^&)E5(CN0ic1TpZYIsW946*SUg)PZM=H%WPNq~V%7sw4k^~B+C?MJxqVZ|8TuN11jF}?nyGIY^AX}D)v8JO-e$A(E9>s!_VF8M=~=fo zX1Fq$W%XfsV~xY`Lp^5ccYj{M@OPh1*VA8aV$xh=f1aUtJXDQgYt0Pztv6k7tLtHS z_$5>HxYs8!Z0(WZa~Dn2+q*Af81YhV$4e3S)QEv{`}d6s^<_uJ8D8IHq#nBMO@<4` zkIeQxhM zqOY!b<$(;>9Dlz4qT|5~e?Fp@KJ0@-7?%BIyBvR9hYPeCSA+Y1v15Qf>b6}O>yhJc z?$b}7zWV^~FZbd(a+H3&!$$sYS?%H4{lNPSf7E@vzI@n23_r1Lw7zNO+KhII=ho{> z^nC|h&VA$=Nmv~-O}81{f%}v)9tUhh1 zhon#VL>fL(nA*p$n67)i*UYHc$I)TBetu^Tw^zm&&{G<51@2?f62mvVU#u5(xQ<~9 zZwwc8AFWTi_PGq$!auiLw8ZczbGY`Dy~nWZW6=_~TQs0({Suy8)LVb~NNsMn=!ap8 zellR$$D)&rF)bS7c8kU`)+WDe(Kh#&>+je(Ur%2A5r6m82?hGL7Oyb;`K4p@%bz{N zu*B!%j*0rgT1&XSGMs;L(Z+Du12Z+@fpAY}Krm>~AX+5Y%7ZXG>HNc0sBSlgn_O|a z`p@~T8SeI`Uwxe8VHh}6z#+qs11c#8?wh1Ges{G=W44?*T^;aMONONmuD@-T3hY_U zv{#Vm&+Es2mAzl!Q-Nm{jbqKlaMv*>sho$~GibH$IUwmx}!yhjA)I8$aVGNUe5_uwR=ZWIgn&{KVoW*@+ zpQ-eVEl*`wo~b2IxLxu~k7vf}VUIk?d#{X7rA(NJe9~`CFJydL<1<{lZ%^HQ+M$}( zPv?VkVi@@>S|`KT)_&c5^0|==TYF&`y3Qn>C-%;;g&&4TonFehgB2~?7&#k9Q=TqX z_QERTqs?O|ju-2>EM{DNWe<*bz3^RKb!FXN6qk#QU3Q3C__mkx2=w@XZ>2$TyhDF0RJBH~m;uBh3;I28w>En<;z#q_?Vbj}X$F>U}@@Qfo zz<(?oeS)4%@GpOtbTxD~!;(J13#QEr;1Lb}P_b~ zVSl=4S08;$wVlTMu``Va21oVDwGZGu9(PL*y|&wnMzbH*8XW_EedN_EjlpA%QM1># z)&IS3zA=2s;R<7Lz76B4mF-&U>J1wiS2ho;IoJMdQf%+J=Lf3ie?34`Zm2__e(H9xy4#`NLVIyic-`U@NJ%4TBuBJSCo>pS!-Q z6MOTWS<3f#z@&WTOOZtTPgk!ApTA+Idg3J?=Nh-)laLGLjRqULS54)$J$ue%V@zq{ zJnDY7+{o)Xf!9Ci<3+}8UytK?Nl!svDd59=>$Fuyzk^0|J8T;TJ)lHhmD}z7G_C#l z^R^g2EJ?_q&(AwvEsdSceHyiBrjG8=j&pY_7ky&Xx;255_lLGr&%Bby{P*EWTaB9c z4&nYJmmr@wwtd0G?Z#SHdOPLl)=k}RYzPdY7-YD*d#f@1=^>nlzpZ~yW9{uz6yi(muY2qW$m;#NNL}Q;F*GD~g;BJ%X za8RFoHGc92il-US#mcr1+kY`u6$BS?`@@@uQB2C5^jw)Ssb&{Gn=SL|sh=KnvRoRu zHIT;p6RJPS@?`GjLRIfFp8+~FYP3t7uhbJZ4G^D5o`n5l!VY5G9`eY6<^daLFf94g zlC9hh9c@BaGdy?ni{_7yjba!&-GpvuxH29lyBD?t_c^!Y(R$vc=P@k#S@QYSA2!!5 zUptfgfF3l_1Fau4pos5zYzs)J}Fg$xMPJKaxERyO`T; z-lRU4ue{A%`cx6O+q_BbSDZQAyn4b^Znt@p+R1-`AH(hRZ`*btyOd(~)Ixbx$}Y#MI2`H|Y8{|x9qZZDkogHd$*Ack!oq&}A2lNeLfh}gVFeX{W&wG&=#-7o7% z=0Dk4^<8o#k4d~v^HG23Zv`KTbwA+@xMLVLv4StbuvN3dFzf(j`=8L)%JxxVyDQtz zgpH@j)}!%loK=LI+%Zc%{&sKn6RhtteBgP={k+i2r2f`i3{$*`@`KcF*=Gz}>txv4 zm!>|}zPO!q4bdpITX&@Ws~EOq8pCfcZJ}~|M;W$kL5A(R z8H)QizV<`opuAz4>WO|j;ca90(tOrSZEovktSO$PI~{YOL2=|mrWNVqzFlZQ=EoLv zE7nWvn_Tm=a%iF6v*Rh_o#l_3bIUH!XHM8|%pJSPeCgyK+Bf|LquK0Z^|RkNxxVI( zHFfmjg{@eAIUYYt_x|8(bHFXN)j<~o^y=`Z=4+3cMte0}ch4JT{<@}`+FZSthCN`y zwlrW{^4h<-yoFlkJcH^!OyElaZ`^*5yRABF(ebo)^PF03RNs|3+%9CJDdi#QMdC-o zt2_sJo|e7MeGt1)h+QzexXl#STh~6^+N9bh8iQ)5YAzqmHCMmgzsjWgCu*lyKflNlboe-AUDWoVA%3Y z8MgdGhRJRx` zg^M^|AY)h(wqiqkKGwVpTQL!at#vYNtzXfat$i^J-%S(WQoDr*hHV{6b#t$@Zf$P7 zb+Wqou}3LS6Knoxp?Y_04X!DAw*3WaO7kY12P(KLs(zc)L>2A%+Bj&%N$QO~dyH$F zg~`q^^50)#Ty^beb=$$?Shsh7pjde?oXmF0ac@pm{T?YL8`m6q%S6>=bcylci!02T zV@E3RDb;qFBO|@l$p`&v^eHGeYp**;ebwVzqsH6)%$%=JQM^sncclxJ!-dJ_9mjc)8|uK#Qb z)#jLE4(e{+{N*Ih{le}t$?hWG1$Lb&>^j!tP@e#5NZIiY859Zk0711sZZ;U@)* zPg_Sz+*^Jke_q)c!WYrxlhFA{8i8M;$v>f1NmG&rVf$+6T$B17=h2hh2&|HVHAg@{*=$B7#70`ABthzDr3*hV^p`BmK$UnU-Q-wb(HUN!#uCt*jaa& zGMc}}?elhApgJCNk8$9g&lu+o>ZO((b$}wfdisC6sa?gdu}_DZNJX`gq!*?AV(pRF zLp8JHn-CskJNcu8J8GA93jLe>Z90E~B~GROgRN-5kK;ZPrr0~hP3gOkX9meNg5~+a zRx~K)P3@Mg&M^JkreDi$=XOaSmJP25?>dX+M2(q;sV0{@xi)occ_1w=A@r9WTL>ez zprhNKpg2P~e|2Fprbzo0u?6iLkV}44%yVo=!!Kq(5Pq?~{>C#Ed}s68Gmg-kKM$zp zZJ#o?9^FU_pUCXl`BZg$|HiuB_ivdCJ2z8gQ=EP9iCXwp`s%AbV*S|J$k!bUZzj2B zbU0xg``?fW24n*7-9C#M{mTJq3d}7|UA>oJGz6sT)Ue|t;S>ns*F>POlYA)5itId(6 z33+A5ET}$V+2Xs+%Z^Fd8n%B#`Teb@&o!qHPWTbFKgD&{ONW??E}F#SOP@=|RwQiE z823jE#js;2c00|rdg0|p*Rmq6A9y)@yHUT+IEqn_y_p-@^VQoU6f@y-rr5=26!6-dr(qk+qr7eRPho)Eu#XO`=oN~i7P4UT1-vgK;4en<^2izu;s-DVwUFY z1)cP3?RFRxBl&WEFCA>KliNQTa=d=Op@-u%FWt~WcUm1#50Adr?03_XX36FORC{Bt z2|jDqGly|4KE-^fF8}C@+n7|}-v7>0iVK-`T!?VsZqn2Evd~={N(s4VgkC#97dPv{aa3DQ z(0Rgd((s$OUJ3m*RZO|_j{I>(Hkcjar?mF9^vP2bGBQ{8G?SZ#m`%lYGR6bx+rCzSl@!_v9PKlAR58owCz4?qwMMYTub(49?SAmwai! zZZ%iGe3%}4dXAdud($L2@b>!NYH{EDO^Tm)DIBjJzi6~c^;p#}pUAQH)%_;x1-?>4 z>M^RTdV9rW{peMthO~2}Z9`++^=Xmrw_=jveBxeX-1h}Gofs!wbH1Js++{%j7_IuA ztB-YkXAljI7<`Jp=e8i}CBxi#yf)(=j%(kt=~=URX+FmZooB6yHJ&(=?s>%SYrH15 zxb;kqx7E04gSo|9z;;$#(@Q&Or^I^6QrT&zkqP!o$R(vS~w)6|P zQ=X3Ub<}@mAZ(B$i0#w^J;~wreqtiI^C-BUWU> z7GoIq5lqzQGA#2z#a}&UE<0x&w_AKgv~ODam+S}Iry#q+<`@0K>zA3Z6U{n>6Z9v8 zpEF4w);w;sZZ_&Rsu2!H^NRF>#xI!lV)q-h`}EcRg}94Sx7=7eXNYbWyU{$Wps70a zDnH$gitTvhR8@XX8-_>T&`%Bg@;8%W(e3LDQ2p=R!}Y;^TTM}u4xC2$0VDA2BsJic zDQ4|{W7?elOClC%*@lWRbbow-as1F~o;pH2>z2j5<58kgn_(Tl6SV}Lh}EywoO zP46B>^^M^+ou}xr$2^p{3$kq--_u#psE4_;rF&i`cve%bdigt_o5Pw9)xs9i zWdG!pK5IVq!ASmYPRYIIp!J2CZ2SgaU1@Id6!AT>^@cKU8a!E_`rFeSZ@>Ab!n9b* zk%z`u`PG!6Nl*5Cl)dfEPXp>l8mOiEF`ll^E(h1G3=Q12d^|%2b>ut zUN>hy-arj;bZ6X>eQ4W=J>OR9;mc1Vf5DV7&eIBes-u?HWLpQZ6@?fJ+ZSgv9;4pv z1bTqK3m;Cwm*e(zuNJ9Bhg`z#>G8{?{i*4W%{#ce7Jq*T=Z=P7y50OFGDMRta7p1- z^P2B)M#wL9xwETz-!-M0>eP?O>uL@@s#H_$c=PES!{6LBli}Y^e8u=?RDmWMYqTOq z4c>hQ-FGu;FX*ejeR_{c@I{}uQgg?g!FS4TZoA&NXyXVy{M@Z!iY*o#GlS2x;lW+a zPhOv@Dep)5Kji*Q&xeNkHE@u+de27ljkp%xl29n?1$;Dl3CilQ1`xjnL)X+k8{WB z?KQ45C2dnP$KEkWXT5vg2(6kNO!owhuRiXiryqR0BKj^F+f);u*s&?n=O=#i zxp_`#D7TYuPBsYL6}x}T7IT(P#3&&TwJi@3&(=6|O|{Ecg&WSkS{9b=hQ`(FLR{&5oms_1W!Pa2@bX!wqf8OU5bX)Yjiyq845< z)g)Q;Xyfi`Wt|^Qx-(g4-jQnes9gA8znYZu-F{zJ4V%-XdtIgQm6~uLU4NnZ z`o@WR&V4?US%tz4S^qqAw$>~MYc`)8Bw zYJO6Bv|cr<9PwLxh7Rx zZT|SmFyi-BCpUk^>@&82;j?FLjK%Jqsqgl;WZUw;! zosgXXnl}W^CvysRu0iBLd2iqs%Ab<_Sqb@Lko+N>O>D5$2;V$}ZGX$|)mKK&XPYf& zMX_G__+rwDvF0m^^z(aOHt8S-r8P&`Ov9k zGn%0%8tXda4NW%TOGA#-2l_f`)AOZC@&rC3x5H;NS3WgTlO0>{vY1IRbh^ibn7a9F z$6iD~v9cEr(Sv{WlDu0*e@l9+J#i)L+D(qJx>cR=wM)zjoI9+MqRwx&m`j-}5!efnOS~F-2X0`P@hCG{vmq z;~HvFSD?>pcPh)@FJ3u=Y7^KuqVeo{K#DJmx&l3V=%aj>qG88Ej+NWBE{`nQX!hJZ zg#06O)8QAHjzfBB_rzbwu2{9=@f_WydK>nyK5wwjbUdC<_n`Uy0^Nfq8MwI5hsMbV z4b!V?T}d_%?8otX#I&u1w^h}u57nKUH)mP!-|&?Z)?|yT5p-{(g;{ zcsACtkYzj56Y>68^;dGe!LIuYb^C>WTE7u}EMc=r`_Q%nWzB~0eN6bi8h6GO@!B)F z2kQ}ox+vl=s=GwZ9JjA*`?|T|%E4?4QU1pE|CjwT*L?oNv1~6PMv|<3vf``!U77cm zc9)E)NE=N0_tLJAaYbn}$XKGZ8DuO`+6>Z8kTxvwkmJ7-bi-lAe7>6w9iU_P?lI{O zOXvBg=xJ{T$rfcjOtvW1joA1133s*)PcK zmulrq(I?iK#`9Xf73mrScCRM8mt;r_-YDZQGyd@vi0*b&Va9czozGO}b3@!lrdbyf&P* zfwN{gD*R%qy-mitWqcbo8=7({sM+A}+j^gHMfoer-w-Tq16%HsZH7D1Cf#+W_-KRY zx|qjaK9ytGH1R2M|CBd!Y)Hji3=@5StQv}#tQ z2ESKk(jC6nUKyj>t=wVuKkC26=XZ2hJ?7Td_)k4^80uhl9HK|f_9?u7V5H$e;84bf zg}raWhs<}#{0RAAwr;h3IqENUvDDF0#>#w+l(*JgipH_l z!?5IiYt7s~s&Ed=v6b~EBx`4=j)wdXx(@~)L~WY&sEIgEcxGXtT6n~h{4PY^Plc+m z_e#=V;X@7?t9~k3$vn2!HCCPASjq8b4M=4Pr{cD z3#o!<&(%b`^#{~f-RkyWJ>Brux~gvPUYdNF$KNQ3=ic#vL534aB4miEPp1r_9zFDlr+SbXMbiy%WttRJ@VDwx?$lHdS^8T*)oABn`#3P6wfdl0Q%yG1;`w=M=+}pn&1UXe&{4H7 z{etVz>XZ#tBOlyDHFB$(9X5b!L>O;WS0QXg%13ZJ#djqv`_Mh$b%WM~&zwI~Z9bts z=O*qMIbBu1bQZs15N|SBby@xx#_dolXiQ^ zel^7+XOkZbJf7lOfzasCcPzS~MkT7^4S+6W%s%f%w?h+>2B)^6DN5WJq zWar!^3_eWOH%k~XUq$r?G>(KT^S7-&ycY>uV{*HMtu=7Fgsn9zv3`cFJ!ZfbPPkpd z77n>x!cvz@Sn72NTd_GFN5XVpz^<2*FywEt4qL*=<;AG>lJ+HGX`@IOc?zxtrT!9@ zvQNUYZd=08f63Zz36sBT*KtW0whP^L;d7C&l-&}hn4De5C1J83>^d$9OFbcBtB+mB zMc-xE8q*{^NZ48fw@cVsGq+3F+M}j%By8b?+a+w_klQ6};hftgY@HFeOV~P7ZkMn{ z1KciQi)Of8!WNBjyM!&8c~-)bXC*9oR>G2JB`kSX!jfktEO}PK|MG9bUy?j4Vac-+mOLwA$+Hrc zJS$;oGe}t43=)<$gM_8cAYqHgS*}Xhk_p@{VM~T^yM#eAO#4Lt5|%iTu*8XkB~BzP zaUx-f6A4S4NLb=T!V)JEmN=2H#EFC@P9!XGB4MkKL0FTpH72)9*jfX(OW0a7pMiv} zJ#xE*Eu3(>ge@F$yM!&AbGwACGvam$TW8Ac61Hf7+a+xA6t_!Q+EfyjHkE{>OqMX# zO#U#BFJUQ@B`jsKgr!WDu$0LXmNHqwQYK4S%47+HW;D@A;!g_xS3d7uKFc$rI%v8V zK{%9WB=5w^Gm>>SvR$5+gynfj_^&=YYn?2Ut$k&{RxE~T$ifN3vW~~1b8fd{xD3m< znZ+aAj-0DT&Xr+{r!ruf$G7;A`$*WTk>hr0OH*AxYD>AD?tI|x2g8Utn|4hC?m)$G zSC-qY+H8iU?M-paz;B6~TFF1wJss{Zb-KKVO!fWt8gT#C!2Mh9PqnXf_npS0J1+L! zRPvdKCa4{I;hHRhvEO9vXjS!^mD}bqf&b)v6RFqeo-dsr%_a33&R0>)kJ{-@9^D5Z zSn4?I4l}n)9S6En_B#Sn$4OY~I0;J~Ct<1MBrJ8DgsnAX=(|>v2CHBGvxD2EzN7nR zc3zt9mZ9!b(SD`w1D=xa=StmY*WFQnsrzJYfqh4k`c$R|t+VEN|68=osNXhG3o7-X zMH}2l!k}}5?l{r-Qa@UG7H*gK{&uW*IQ(AiS==u5CVk(=JH4@hcNDmf)S;-!j$L(a zqFvr)tL&aRz4JhI-!$^3Z~B>&Y9-cb@hrDX*y3?+moVMIx9=EBU2Mq{?k{z*6~pIt zsf%&G+CE=vUQKJ0aAk7Cy2r=;rL2(h1h|a>x7>%uwdJ^!6~JK_IOIOkzJ)9@aMv=U z9e6W zN_imPUXXlm$vIx5+oXL#dX9MCIzOHGoBls#4_f_M|6A`iFl^a4{2gf{SZm{U zS-Wf3+0p1yw@ckC`AqVJ)RU5~E!oL)S!cm8;lq9lLh3Z@JvmeL@+Pe>iX1ZO(a6bbKa0=(yfBhqe`O@MT7!6>hZaPdjlYB8-J9Nsc=Luw#=h3H zyvOqEJGy_9=<(~gTk)yxsoOk1=2S>N= zdN8sl>dAAoX%HP>dUs^UfUndcD{DtrtY2e>-(3?~blNYG+Xnw)PJd=&b|F{BnD>XpIrkJT+%$q_9)XsQaNU zW$SjH62Ix;2GLP9SC#GhqG5d9A%{f|&bhR#dQs>21uGAZPFgoD`qraEBWIuEh|^h| zIP!)_tE29VQ+w~RC6T*cZ_4d29k)Dk%k8&u`#qP>i#&Nk9d1A3(HA2#ez=_5hg`id z^3w_5l+iegR&9zL{KmLA(ZJ9e$HiNo`DLWv)X~vF^$&`-Us64~W^`fn;AiW`dw;e& za%tI!=&>)YDT`iOKidEK(b1BDpO>B9=iq2zk5SPsJ?<&nea#Wk7VC#aoBZ0Ztodcf zMg#QSSn%6w~<2Qq%&IV`a6>V=B z9b11y^p?)!^1eC$wCGVg2S)pC+?F@#a!0h=tHYxopRqje)}I~GM{gY-Z5#hS?}!n( z(Sm~qMLYYWk>}5GL>J#OJeuFl6FFsSZgl4PBck0togJw^tW~srkHOI`-7bqdpqyE~5&g$E@8PdGD9q5o6})XwL&TMc%x!dX(_ib?YY)!kfgQ#2dAj z9&}v%Kj5zq=N3i#HGi&bYViiXBycyaz>PR z;f{I1Cx1M0O7uUM%+Gu2_+^nVub2|uox3B?yJ~6Vq5n>azPYh^*025J=|`sncJ+;;IHBhnm?kac6%=Z=5~AU)NbRAU>jEipZoOzdCzW+{Pg4Q2+^lSKT(1$8jA`V z<31KmMiyOn&KIO+A5Pk57lgeBFI4U~Z|9oUsiw5O1 z=B8F3L|TnMwfsrQ!(&7DMIL{#3AeBO_=!lrI_~nhtwu%TUs0rk7X)d>4Qrj1KW>cu#ykU4WZ%NIF|K>#d!=+2|hMy2B zfA+QE(d*jXlXv6}2lsbh^{}qB!CM|E9T6?N{ynw5sAc&zhmVYY-_>TE0?^!eBMN{H&(vOy%!?0!l#D%P8_}T>* zsqQydXL#qUwPKrBzEehh)-N2O*Y|sn^=$E*p18lo={!z{mpa627u4Xnjy_;zyxkX3 zhKC-tCcb0yZ!Dv1{-Sl-JVj^S@agB{cQq^JJ~n?*d%LMG#%&pB^~t04H_RQ#?F5&- zc|m-|1*hcE+Jd-owQBxdirQa3r)T^g_r2WS=*%B<&k?8bcdd1@%-_~l>)iPlbGx+{ zZnyTx?e86XOnmB;zT9r%fycD)!|gYYsaHnp6zeGyYhb)tb1`hKhhb~YTIgBk7hUVP zIBeJWU63;;-@2`=(VREZWDAXN>Dl;KkS#}k4B4{giMW&}Qic$IEd9%LV)0A#8rTNE zHhVs@#N8k&_$AGLpmvL2qMyOu=r_i6lHi+7^qVPB2>^f~im*jLg9qj4;LNwe9cEk}JUe&I1K z9$`6S@kNB_t@yYbu6g?1g1=g-naVI$mE%CFfUkg zDNZ=IY>c>+!&3H2d0P!KcEOFYNO<~cKBHec#p8Rt)yir8YUqdYIfLGci*<6JBRBsV ze|pOO+-|Lz+ie|p%|{cXhfi5pHg5dtNSElOXwK&+m-P)i9hvw`N%TLhhpEX0_eY49 zEIOC6I!&HSoi63Sl($k}NO}88%aPF=FI^Ow@ldmJscUi|Z<~HOJ95gayD)Z+qTTGm;N^D`o7)sUxCKH{7gBi(8gUohW6j)S*v8-ma{dSJuhV zxLoR7DQ~5YE_!}cbk=3M?AG3gl`n$4jqa=)|MuS9Y5HCA?>Q%~%j?qkMe+e7!*{oe z47hC_!=L%<=iUAET84$s7!f{WhoZWRe97--<`kceTHewqaL2eFwM22Wle@@4f^$;6^{d-w@a5YO)#zV$BnN( zNieOyN4M*^{n@9l(J#-RQ%3Ey$L5Pxa{J+LkIp-9(1i>)Z~LIKzk9#0rGE92GDZEL zopo{9q~q)GcU#swzN~oqC|-lPbadI<=d5S=(cDYR$fuzGPZqphcI28_{N34i92Ebx z?h@`Z>*&Mdb{}dl+c=2((>Pt6XD7e=!z%a=HOD3=!vt&Yq z+ATT2ux;y7yKUdf7>KaV(rl_1mX3;Ez3{Eby{l_Rr9V~j$na>_J>`-1#SNp<=Nefs zH2TTfWsxRNoDh{h+Wf|Yqid>P68W%W%c%6@j^8~nT7Ui-k*jAoqSDvzlT$v) zTRz$mmHuFhU4x z3mK9YrxNlcF62pC{7T4^xR57tAw#&o#rxdHk{fX$H+XzYR>XyT;PqJa&ug~mpVw^3 z4c?#f5B)3z^C7 zmR#ljmK=_sd`*q=&X+chKX7!b^0!{ARo>U=9Y5#llggW4QLTLLreDf_y}?ob?IqR9 z_nh`i*}C_RD&KzWukr9Fx0KC)*;(G=f}i3C)Lx;FJ^94)KL3xs_kgM*>H2=jAW;NG zR1`$aSx^j|)4R+$V;*xBvtz)VbB=>KN6d;Mr@P7=am+dA954rN@3uFT_kOe9_pW>2 z^?vWSX4aZ#e(V1feX6_n{@3p6Q*|z;@KvD`tmW^Q7V+m(c!$u5){moei=W@#@%gh` zDP_7wij+ZbdGm`GmB7Ve;-JS{!{798fl8cLL{EFm3%S<-$N6QA;}m%^18zMiEsjil z$LkGE5Hyi2nMz_y2oWfSeh-kXVf(NAMTb2Q zP%TKfRJ9jheQ!d~R+&ZYO&1~BIIwFY^mEMaDrSc`viGqW#KJ)y;;81sx@Ha(hsS$~ z{CP67h*KZ=;)~wmOSi1-+`dd=NvX`DkzGDkAghbGQ75zLUoIbWUUQbWnwMR?xmb!F zdGvd;yVVaZOhH zeJ$nhwRwfeT9uu85CRorLdBV6V1F0x1l1FBiQqovnd{e?P-}j6G3RnAHtyVJnA$YI zn0&N4TfJu+oIe#R*7aiS> z+m2oC;URu3_hHlX*s-78GYXrMgWXu?!#2lwh(fD;*v0~Z?EO&}VISnc$`^8HW5+rQ zFUu=PjLFCP>v7fl!6mqNE(_Z_!A?xRsNb)@3bQMEZ8%`kL9i=aoKzbGwR%A-WP4`Pt<4bd(P9CJED1~$=eEu zmvvIP=RhAN{z?JS=R_(#t0qWuQ@nnS0^+#ePad~nytcnhUeWYn3QydVpGVXU72A@Z z@fUTw@ar3MiGdYv@uz)e8M!b=N#-$^H}giP^NWHrzw=Zggg$qZLOlsT-y#tWzy$W z((5s9y)#_P7OBs-TrVr+@pOUjSncjhKY04{S9|j9d5JZ&o@e!WnBcPF*0C^cX2&6l zvMSQ}Eb{r-%gd8;jT>PdbLCgxXk3{4<-XN%M`zox8^6xQtVMqtUrIdPro`pgp}sj4 zE~Gk^=RfHVC1-9>r`-$}Qr*ihY_0%txt7_=pNJ6h*eBJ`yzYnQ(7sg%Yl}m=&%f>i zqw-`_YVR&1|!{(=GVEH zbuVAkBO3%Z)937cB8+o4pILsZPqy6L1FaXnR%&eU7Na^QKtj`FN_ZUyV;h_!skiMJG=_Cey_ zfYabuce7G`Sg<&pAsJ5kUr~G=L&UY-m*K(el}bC;Q1Q3NRj4rcZ)ME9P%-D+T^P1v zk>b)PkC@)?KD1hNQfcH7CXO$C30mhPO8tRhqI$|JkfO$<`KJ- zPr>9@3*dQ=TwfY?!`EVdqT6_P-Z$3{I2)c-d}{hmySMf{)H&iWQkn$uH=p;x zp<>y@t`8nuopu9qT=EzBvzOp|Jvcl#nN73~%*iX?yAN&~{lxLAmH3D6`ni|SCe(R# zxWo1p;CCmN;9c7B=U!`})tOx4{m}OO`O#R2UmYq^b`0d(dX0p;uR_J?$}xOXa1rR* zFHGz_HKBWBztx4Z3;=v_K&^9gUo$3wQED(#NS=)Up-$tmAMliw#`)} zwAiKg>DrCw4{#CF+$LxywTqK@LyL;u7ljuUv!Om<>vn_Sk4-v;rTtsRBppG(Q* zJB{aa{l;VNmsPgp8qMbvuovsLu2(+KpUK<3{=^UO-=Z9DJC2{(lFHw#wudUGXY!2d zCw{P9s&aPjIDTzRD*u)vKV(W6%`3mK6GvxLU6Nn-Bft{?K|p@euK+=fKbQxwO0^T*UU)E8xS*SGE_f9^!6|<*+iLl;!PcXHl)? zdT_b+*m@^kALHt+fJ`@YS&Qel6VKnqL&HZM6}J{1B5Sej@NP#9#b--Kq1bkVLx%!N z_am9br6~s>&mKqR3d<%k6+aB3OM>;|i6Ak|AsH46y_7m%E zhJ_DuimvbPK=|Y3;4nH^1P{Fmg+A?sK3ju~<7^B28)oLnDu#7B4fj@_gd)`g4SjLM zJ6J1xM6J5|yOEC%!9Unf%vM)Gt+IBkO&1TNP0hdD*wm%YBEIBU@Sp0=o)vI1^sn&& ztZlA$+_Ob}XuU2Yn>6nyS3Bp1F`Hh zbYr6_`tP33!qPYi0Hd}An)F7Bs6{Z_i)%KGx;it<$ul&2xx>W9`K5@GsiO8(Bmxy++#Z8^%YS^cC(U-f7bdDtyC3 zFOgTTqdcy*<+*ygiOh@UYxlEt;+wX*ij%!PM2F|&dG;K3V%}R1EpGU5-n59L7@E)_ z(N5374tfqA$n7UG&f3gxT)oaQW+EG`=EpBTFxYmL^X54?&oMWcfYyW(AUOZ z<+J|&>$h4F|Mp()=iF{;C;nQmC(E6C#(7j1qy1yAPrOgV0Pfy4uQ>fEg;(E@-_S?x z`@xf5&(U(|b?NO)Ke^rg@fse-tX(7JI##Z4v5on+igQdpaa^y5C!F*I)XmQ_YUT$; zQh02pqQBMEQa^vIy-_#M3+C(f`JAPRT$y~_IHq|WK(6hvy?L#m`IQUG0{w#~9cV)tYa}Mdm>F!jILa z%k9`ek9n{z&j$5L$&b+P;W8*P?{d`c7S3$+yXDZmt+%yhYAUq5t3M;&@>a48c4X2V z2uO1vusv}*+zwc-z<5`dd+{z%t>x&dPrO@`c4aIvyBd^=_GQ(2{eVu3vqC`^f7a~yXK2^;q2hbcpKTfZE4DLT4`TtT z=YN||z{0Eb$Lauz^bg@l@Oy%hjumCr>!;x=SEuUb0cSW)l%@Yj%BN5 zd@p5+K9>@s&!s$?|A+OqKF4yiK@HZxUKk!?{;ej@!{FbVpUZ`Fh{(P>iXDiWz)?5* z$*&B_E+(xi#a6^_F?931coTh&s94#2EXVq@hHhR5UQHiE+qUlok7wl(lbtVv<-lTC zJuX-jjXMl0oHoD*?;vq{-!|}kx*M{@28a#XLU?;qgN4g{#Es-(kk9ED1Wj}nQ7!7h zhbPCOPo58aW4)qK;N)>|+;oQzob3ZX$4=>U#y5CEw7T0;B|hk%yeZ;w^LBB z=1u-3Qzm8olw;5`;vNs_J6mbf@f>uEdCqe!&8cKPcLv5BdCg~jn5|55xv9U?_{{%e zIh8>*uR_2d-+9>!)0N@2=WspPUi_@+t)#Si3|GH8h&&C)E0foKhabZo#nrjDt(Ll< zAb5nc*l=rra(9^v+o^x|FJ$Hn>)U>gY{)ccG0M_P$tt{9nZtJCe#}El^J4BS?_5XG z>wRTquRcb%)5qw6SIcV!>N&E>Gu%X{&;2ydAU8JctF!1iS<|N7d=Dv8y+yM{ceOiR zzC(rWo&x8(X@0w7#2Kw`_#DU_X(yz2C_E1blW@B2&Nm!y1iPxa33)Dk z)lpyG$UO{RzQ`cLoBQypA&->aO*0tBG2hW}kKT^FM&NiQWr~Zi_PeFk7?Efl@Xg-n zgZWN}zxcXN3*X;ey*uRdZ*#dHTh`RVtCrUWy?xI8;uffxW>3;S{&n}aIpM53ofYfR zz1qRo7k-;Ve%-IXvi`_n?ZDQvylTw^#rM@|El1Fq-{zb%1%FaH_Bx|&e}DS7`RY&m zvOx(rs%?R~17GZgI z!_w`w;ngcIaploo7&N;+^eUp)U8T3c(-R}0)EalOrOsB^J9iA+`mR5#PFe>!drg6( z&D})JA{$`Ztr@WGu$TC2&{9}1Wf5FXb`lS|ErYD*mcptQ?&5UDSy1`sda$Ue{GR6= z*qLn&Xe(2Gn;Rd!Y#EflKTki_PyWDhJ=BPt2zFWY=aEj^VZB{@2yg9boG;C*$EU0T zzvb0<-vyqAPkxzOitF_)n-Fe;j(hxgPCtM4Dmoeb|4=oDuYpFJa(NQ9a>cVT`yJPy zfqNA#Dl{iMdF(EX+Nr3Q&j0fB*{7P@d|QRE4)dPFk^B>t6YKIAe{0t2OqvHUbhB<} zkDXpYlb@9!aCw-an>9QOEwcp*cAp2s-enS_`|pQZY#OXx5GWEJoPrk5+d$*m!9p6d z#E!mq!LR*HWqa*BY};R_;Yaa}mbu$P)Q|AHO)=o=0=)qn@&w-oyLp6tH zE^OlZkx;e$Of77O9qUnHH0+Mpt@Ur^!tx9p4^g}CX{A5Avk;eQ;GN%{*LdT_It9*y zAvJUJ=N>-n-L@srY+@O{tG_oJ`(_R7T33x%obSn2*>*zbpoy>`n?8T3)-qArK`LCE}(BJe_AI99I|<{7sKpCtM>I_+S-il%>18x zcKVO`9^7CDBn-a=TU+>xzTHm4_{AroUyVS~{J}w3()Tbpy$uwb zhFpc-Uw6Wz_#hD*pA1p&;$g;{Ao1hV9f-WT9L|pk7C0W6aUzab??c|{6>N<=gc!OR zW1_{ZYmm8TH7!#>PSNmcGW1DE)NbX^CMs1r0tfG{(uyW#6X9ESL+J!Z{-s@Jv8(+7 z=sI$OHa0k$SYK{4obLZXTUA*f)4uP38FuxwHF>j&Rn=C(smLU4{u)2=?&(GdZ0w_H zC;f#@pIh4TWUltQZ${zxbTvfmU9DQHXB3O|Ig){0R$9Mi@fKwZ#6y*#0oGCACtk$u zf(zBk+iGY5;+0(zG&naxjSkN)R;@h(o01=@$2$gzF!d}<4-L@<)y*khKRN()qM{+8 zOjaS)V%j?8S@y+pWtYqD`1yf3B?i1w##&?9dqKB2O|c+_+aG~HjF zyLI;zEpsh_V$&k|i`w3z;`F)jxqK+^^3+>6kD3ZsQ{8#WSuf*0n$HNL`hW?rdgTCK z;--@rYH0>NMor*N52W%7b&Eoy-P5>j&{saZY%{nsast1n>$8iU(UzB<1AWWbiD7d- zX`MXAfRf2wRLu5Sb15_uE?#vKW9>Y-N1LXwsGFAto`urGDJ|a2Q;4 z_7PskeECj?FmQUAK{R=tk$cU*t0?`w#gwK#yrcIcrJz1OI^T5Uz6S>>vHLv4nRVAS zOUO8DXjNCyqsuLAap^>B)vxvGgBIKDD0uB5HwFVcAO_(xRs|VB~h~`{GADYrbqs=yt9pT)E4A z7K~EZlhfLwuQz#>xp|fD`>tv6{a*8v&BiMWT^?zVU0(3!?eZ!~(GI+)-*;Z&%6NrE zx$$-`U-+G?Srm`CIr-g74q}4KXvKX*82{47Uet?tXPx(#g->YeEWCR5R@S*!v4v2oeq zW>n}$!&=L-X^X1}G6C8xY*T(S2%xBuoEcT*B>5&krnnCJaP0E*>f({hE#qElR|vN#u2HS<;{3l-P1>~dcOR#V=H*E zIg{u+FOauT^TNam{-XS*EPVc{>x$b}ecpSR8=rdh4<*jcS1fILS6fr$k!3-mr&zLh zrFOftn>Mh6tMD99LbHtkNlNi9Zu&le4!Vr|nT zMcel+mEV2Ctn-62^9B2z#K`u=EOVQb;aV>X6Iz_^_R>Vth!Vvi$r7?aapO ze1$bZY4-B8Hc+o~erEWf6tG^@a*e;vqpN&U%Ep}0Vp2}?{X4Tk>xxIUw~=Rg-f96b ztZ1^M_}sd z_G4or5@BG`v(~Z~1KFd;jUYIS(5C19$Wy*Ifo7T4YmFnH^1(ABq1%%L?fRk%eCXu& z%J8@s+V}fUd0h4PN}d%S{7U{${MwQfrQ`SleBf|9k(_B1bRX4Ad+zgrzk1pVVrRx_ zd7j+h^83drjWR12j!fnKs(yni^W)Vc14i?j&+OTvYAdzy=WY4hZf?K5yWIWNj^C>l z$yaCcVedbb;FfJ4wafMatnqj|QF-JSE$&Jd_TYR|el4|@HYGKPZGQ8GYf&w<-n)a? z-h2J@?;569?-vba9sf?@c}7mMHXN7Rc=xJ>Oi-5S?_SHz9;v@me5*7_mfzv#WglaQ z^NqUC_p2KA+k0O5U2uW1-MP!;g|L2F$ZzkCt@^v>?{_Z2((_r^-Jlx0%QOzTt7m6J z_T}LEEHt$4?Z=iacjqhH-PFHV;Ljqj9nfG$3fO9Uv(&kNX#+DngK|&3^|eB|v>RDJ zL+mnlmeBmPTDkW#82CN|Q*6bp6Fu$o?aFf%?sBo93Sr9kW5VX(i%b*Rx7#14B+=m^3ucynwywO1{PElb^?nr3t7RYL3-w*k05pdYSpJgt*7i!o_LGKgZ zOnTNxWBlHo!#e$$uP=u}v-PpP<6xLpZyy|K^9~k%9}a6b?S`)_AHloZv!GL*wXkd7 zdq_Sv1ICry2-R-?gjJW9!mV%fplZo1U;C+db&}>@Zx#i+EV4+Zg1A=M=Eq0a$l))(UrBU?7*5XU87tZ z;L1E^IIy)-?kV-w{)8v{99X|=ca;IrFJW#EJC=3gU8P1VeVvQrCwRBn72f!qhr%=U zIPT*Lt@hl6#5`j1@-J?)pnBTsiW<4HWciyaG-FZ-|#F1z4FpPrC^zb|Wh zI05Dj835l-Wnl;QsL-R+FxXNpI}7M{3?^S04Lh#nWP6&Nf-XfDfoJ7hY^~isczZ+z zPyayHro%e;Qu8u2edxy=KTn72YhS^o>0WG7kqHp!bq~(yb^f%+BjH5$i%>W|1G7FK z4B2ZQhYH@#%;)X`Wk4-|_M+BjDE`q-`MaSfyZqFiX>++Xo8GVGhrdJFVYSuoj&5vS zgfokIa7dk)!;9T4?7*(NPti8-b7AYhxiD*BOKp>#1Ji%oj7?W>YEIREfavDQ1|HML z-JA~WXaRS&x%6z9JvsrBHoLQ8xAk{757t8IXdia7{!Z8xFbUX4e>TNIpI2-!U4I`J z$Q`)esUd_4s#+hk(pt1STM)&laD^I>;i%uDUUk5QS;(=ZBWBsOYR^m3C1lR6oAbay|%GVyJq3xUN z@S$c$h5bS=$+Vt)vxV`fy6lu{}ZQSDqd@1D0cJuyPx#u_G$XPFTM7-9# zVlF_T1^%q=nbQ3Htwb2avor5;P56lObD`d#5H_-4Hy%^6Aso1!hmCQl$ls2f2|H>A zv*^Pqio;g6V5Hnv$Yp5gEm82mhlb@>Ch_q&==)}h5c>%%xV z_H+F%t@U5`Y>P5}gIBhA?f9&2>ebJ>*A-~1Whq`jJ9FKuLr^}cmgzyG~i`-6RhJ-%11FHiifefahkLY`izV^m`|?! zq`s#0_4HxNo5EpyYhQcj+wQV*^GX5krsr~vJC~FVgKP0tdX8_rlnv^6RpUM8I5M|0 z!B97UR~}N+kyZMm6igrAfd>|JVh)eW!~NnT^>+dGEXUW1Q2hI3KJM&yP+Am$TFKLR z*>+!{1>}QU$)ov!dv>godO-;fn#H&1?Rf1 z|5{r$d$DzelQ(;m-QYUH!*iP5 z%sIfiyRhCZXK3H+t%r3%c5La(%UY-53t{PF4_1HW46RPXc<6M)nJw7+NVD;!5S-hW z)qJ!_bJD+~_`aqG>#L+{5vgnR@3UuS8<)guJ)AX2*8TJF2Opk&!xq@(9LUt_r?mAg zPrzU&Keq8%Zhj|tH=Os$&O)}7u*4KzuJvDk9degZt+}cuXigg+z?T|hl%gXaYUN72 zfRjrvD}^3;@GthCpjO)~P_tfMKC_fP8#%BLOe<1}H?Hf%F4qiI=M3!4_k49?*7DW0 zCDkMOVAX>?>hhO%sLNN)@t_Zz`02d%$hEV!yI&?&#ovx^yyR}(Rm_h)ITFB&e;lJM z%NxK{9>)7l?WTCH%+4y+D8mmW*Hlh~=VB$Y*W}Ai7Ey|93}G>9YaUuBld|bUZv8up z{djQx4_2jED7&WDfrSRXRTk=X;F!D%v^+gmLqg~WaIP1q4Qa6lruuwGy_IfHT9Qy(q&g-cz`>w=bbp>*? zZmz)H6Y!(PZRnV-x>9J{F&LQp97x|=2I>3B(7b(ve+$pVnI9Yl- z_?PVfk=0yTkve-|Yr&E*QLhs$O=ny4!4@sR?uz~e&!Nh}MyIu>1#dz9*_V|kAuqMp zd!EAl2!Htdr5i7~?lbHj8wRXUD8G8gp0zDmnXerhrn!~N$!feV!519*sI9jPVD9B| z@d?>(XvsGM*hD*fzN<te9a-+cZPuT&IRgWGT@R?$8W{2P3(xifh!1KuNA4>I>^PVKqZ5qM+K>(!tsT1bKm6W?7} zkp6wwM@xNJ>%4khq#jb626?gadTnr~2e-c1=gYq9^+W40u543XS@CEG5&k>?{9o#S z`^iC6oM?gWgPd4|KL5O7WenWVYqMGX+{BOyvmoW%3s@59EpmTd5BUaMgsn~d#cSXF zU>9J6s4juR$ID*(IrJ;IU&$a!)t{?IOnCw4A%jSL@yRwS>K1Gt<08(F-ka2_<=;^M zyuJA2{l>)d&5lCPeV^nxf0$Lz8Lm2=)(-2}P+p_a)n}!W>)}bQj(#8IbsXDr&9+9D z-=ej$zXI}F7>)-B*`hs2&c<_O%P49^Ir0*#&g=6dSwx#*`T3j% z6|^z$vWx12EAa=HR$3D$2aAju8uN+SlavjAgo<-LoAP^)3&H3Qp(5XjI^6Z)0DUir z+@f~5a=i7t#qi}qPT{TQjP?o?4l!>0ojL_lXZVZJq1pMY zrR~6Fe}HgL3FSMV<%2{eP&B*}%)@S6P`d8PD!Lr-mfC@|lu1!&B4; zb|HL8A9qnJ>wB$CrG;A0eVJLs6i@EBu&-^d{vDe4`-Ax7z7y0RA=z0!#}fRHk1y3q z4T6|kb`Lpe5J#6_Fp?XV>$bc(MLT!k2OJM8`o1EfK-)_E+MI&Wt5t|7 z{G=RzJh&HBJ(WuwZFfj{F#0F#J?bE0UuS~9^8J8AwH?K$=@IZa!xwP8;UwB#YXh%} zy@9eWZX#FoBskXX4s`R^zvCae9LCf>37Jl05)U7G@Sh$Bv;t!TSi-R*O6_tlA;(}l zk)f;)3<-My%ir6Hfd`91o>NbtNP7oy?O`-zZFLtWG;i->w%38K6QiECH-fGcqn@^x zg02&zp0@Xbt`nn9`y1fr5~EK08=y{%I_++Vq}Mm=qvzOECaPHXwGJu&LE));kS)XlZ|40U4EX{|Bp#HiEyV$_LI zr}f3C6QfS+i%};=oz@hiPK-LOBSxJVby_=&Ix*^L>xgxo7Ol$hEjm)$j89$2{+t8YN)QM52HTY6{eGPtPIwqHnZ{!QDDMpa^bi>cpsS=5P9r<_Hd7S=5P9r<_GyX3AM?BQxbJwkO6ml(VSIO!Xfsn6QfQ! zi#jptl(VQ4qfR-CIx*^$v#1lJPC1J@G3ppIhxGkeP^bJwopKg+V$>;TQ71;7au#)B z)G23CCq~`O5#w`-Q8#nS(1}sU96YPe zY%eoi6Kqe6ZRnbyPK-KT6V!=Or)z>bG3sg36& z6QfR^j5;yufPM(Z9G3w;Ws1r9&`3ZE7LiIJd-kGlPb~Ek+ z>U580JqQzR8o%ck|Fju8-6Lr&t+Do&bo^VoU)V-wx?k8vX1ZV4o>*#Q?DL0r~4?c=S7|FqrA3PUeAkd=$^{!cIEvCunpbc zU9%j7uth}!F%H=RE%h(~x7k%yRicjzOl+vx0t34uxygnD#@a}r-2LY*NrFA;xe%r5L z*@@v={#Ft2R$u=suMx)eJG761yrx)QcP#H|Ag@!F*D}ld8{l=IaSfTqGI@Uk)M+g1 zG1xiZk;OZxr>58m)DmvV}N zSIV;YsdtT7qA^b1-vIxX#yE^I+TQ?m8sktWMxDku)QM52F%ETNslBo1q1j%Mj&I}| zjeSxdjNGL0PTm6n|CYwPf7%ZL+tAq8;Xr^mU$rm`I%~&Jr?F4o4*_)=`{cb5_{zd5W7~wlSkp~P=FN< zRsRd-UXL)^)A%@MeoGjzVkYc5Uz#PykA`o)eIe7DNY*iM5@ej-1X!c8Mtd4JXhJJs(s2B z$E30K{`ZCukdV=OdrWDT@n;$EX#Ln$cvA%H`o$4iP28^LJQL1#`d?O_4n3gWOb9pr zmd4+Y`ge_b&Hkc|kJI;|koH@^>qcYoHhukCDSiD~t~`Op-_lqt@3(;M)5cnZ^LPPUi6E`I&1=FriugEisc@9W?3m^6;`>NFZA zDFcpVqlI4=V%D%Poe=`r7suowC9H z<}2W%zMdWbmTF*mFN?XWDl2yqYpY802)1U7YAyD8Zqk6C5zKi~l3KQHfD(AD%x`;Q zWUpF7%U*T4axiDv-}cS0`#DN0b=C<=zAnq;eKsCFAFlCy5ismVS(fwbJ6^oPV92TO z%Te%Q4Xs?$HV_g(S$ zZn9i?r=KH!7VXm|UlVy>uLYY_>zXn1lK#-Io3vLCj6YIY>GG_$x%_x zAtDY6bvOmZ)9+7>jrrK{jwGJ zW84&Gs6yc^pmuEdZ-s+CTqe*!UfQU)@gsEd%{r{=4-5%r*S)>f1kk z?tiAw|Cu)bbo~EUI{uDyqo1Ij>lv(Oo{?x9Ftdz$>`Z|1`_e8;3ac%n%Bc9eO8DC% z|3BaN|I9i5+s3%)|6}~&-~09N{rWcs{*8hE>KIUejYass|82F$zkT=>=l@=Zf3L%T zbqrXIOZ@9P|I7bpUx)wdSo-()|N7OxG4Q`n4E*co|7ZL8zxVgw`}%3fSsNGoAnm2jWLGD=)ZMsIdx&s|AY14TTlC)beZ+v!%pg*{OkADW!8T) z-B!5jP~$i<>%Wt2TX3hpq02m_`zhPU-Q5gbX7e|>4PEBi)dJM1W9t~Y%=+(YtK*xL zGjy2)s#aFB<_$G;nf2dRSD)>0Gjy2~j(1UKyInNi*~qN_M!TB)eukmTe52Yhb*AcX z=raG){F>RuhG8$Wc}zo>+3bU%%WU>p#pB3qo};15Y+e&Xm)X3AhAy-Go9Hr|_sD23 zvw2SqU1l=|3|(e3W(-|sGsX;EW-~qwU1l?e4P9ov&zAFke;B&V+%BiJSdZ$4F0=kS z@zzJpN*KD#vl8lBn=j97=rZfSF>m!A_u9BGGT(35+d8qsHba+L|J{0P$v)i;UFHql zhFcf+xSxdgQRaVo&uz9bet%tN^OzPqj?88s3|(fk&xS6ud5%^*j?CsYF?5;DYiQ^) zo7ddXWtKhXe@Z{)KL6{JzdTd+Mww-Alv(yhng8|4{wn60?2R(Z-YB!|jWWyLD6{O1 zGRxj5v+RvB%ibun?2R(Z-YB!|jWWyLD6{O1GXH<^N%GH$J{4>~y@puxWVmPeD)|zA z$79IP{?;$!I{)UMsBdf7!+NRzmf!pn^#;TLuy&gG+|bECQLj_6mUU}xzu){5^$C+B ztf8HX{^p;k_imltdeXPbZ~lq8(}1s*?robJI{6ano+j_c_q3dyF)5l^f>U#fe)PJhqP@i$oUmfUQ(`Zli z8|vnFX$+m}H`LAV-WWR7Z>VRw(nxL9!^P0amr!5zw1c{G=-xZFw8ZJ6J8+$lK7#qxA3c*zh|&n6LN`)o)UJn{-TD$r%T1-NtlR^?kH#g|a5u zM9mIr;fv#Jp9XlU7n2*R8y9^@`aIQB9THeuU3tEr?a`A6wM3VuB8Z5K~PtHthQP?auAlCIrvtoF_9s(yWw!*=UljCv_D(Ux;+)1=4`F{;z!UAA$S zD@plytXj!0(l+V!p#wQ~#j3kbl{DJWdHm}AfBgPe`rD`mvYv=ulLV%xQ)oAqp)MBDz$8Il%e?rFU+ zG|BeImHkmQH}|l%Zg;>oW?dUgr3Rg?YSkoLYp-n<=dUg4_!c^Ts*|5pbT4n{ZpR8) z?>UDV>{kt}+sb+voNr%LpJEak%w*iOW>w)W|>+S2sc0o&~4&ejqcnpl>XNwPg} z+{3Eh$0#%2j951BnZc4@{Z=<(?3sRL!}hX&lE-2j)4PmVHT}wl*U;>n!DgS;*e)LG z!qr8sO_IOZ8m;qCNAAmJEw;*E9aBF-T@vSD=-wqG)H_RWTQ27IR>#zeR=tz9Sc0}) zw=HWNtxkP6)l%T;TU%WDp7`B3OYiL~ZCUg6#GJ5PI~i|#sK;lugOj3y=NGa4{5w`1 zQL04L>$@fO{{P4EDYtoiG*%ryeMto+Yox_P&(WBvOA_1Y-@Q9hqNjQ(y2pV}eD6j8!LO&S`kGe2y|p`Isc-piRmh(sjk4-Hpf{NzbSS)o$Ix)oUAfS)NZ1Qh$7GjQ(l~ zzvQ4!xfP>M%dkDFk9~qoilu*y!w(6u>Xdpf4n%oHTF?*CcXj`@+_H~Wb+52}KWt|; zKg*_nmNnCg2qR{4e$Qylr-iG1*MG8wc5$-WGDoOu7kj9Oq8?fHD$#0_+*fSqfpIx{ zsu%LjvaNLQW!a_2_Uc34wvB&3*U$HVoflHfBuTMklVZ#!#hy)yNh2OiUp3;v97BzJ zDbFp?{6pb(o@#@*LADRqYFT6TxW;kp%)Js;tyMJU;DLVEJgmBhs=Aj(>7Htl^4juG z{a?N&7V|MGvS_GnwLYdaRJvJSy_jR0J3d-1e))t&&lj6}p$K(DF+c0YsB^YiFFn=X z`J$|$ACA~0FSSWtYLmP)$&6tu#-tg;23LIGsh0b*krhAtW^A~s|7*oDu2FwaHLPVl z>z$;EmVBMVRm+Lu)><3$TK)|1RQLE*xAu3NYw?;IuI}(FXf51!m__Q3<;R3@wb$Zc zYqh`)7Rf&?QvWTIms%w+wMemQm3);v$AX?C#jrJXzNdQT@G^^KU=@ALiop89;<`Pb z)!M*QMK4)Wpq@1q!d1O~vz%E|+&ZwUhgz`gx~RXlHMV|@3pc!1|F2a$<$?Dr>1efR z>yEeKMr`Zm011uujT#DORm9e@@c0A^HC? z_y2vzUu6kbI$TZ3pd0?Vt#@)s^4%kLdIc-_@ zys>)j@LF5gfO{5m+_dTArlm^$o~pfHR@>gan=Dd4|1poGvCk&upe=lPwEBLQlcnQS zS5=A=)f~$(t_;sKYZ&8x$zZq=Sb<}?xi&beXRwchxCBIdrYi^VL)+YI_P4Zj4zWQJLZTStc z>a5nONh_9Ru;5su_Xq1hRmw+I$~!eMdo;#aRBF73D%B)5X&m{-c#~q%V%AYMy^gX- zHL5D*uPS-6D%Ccs6iYVKZ!uQ?@pI8DX^z*-S$uXguD@Aht5VIQN@Lk?$CR#>O^Sb; z6!SJ|tW>31OO#V#a)yH2P^i7pPJ_rb?csO7)&9joqqroo!Ms z>*r->NAu)TUQ?}$Ijh%NX?3G2jg>|}<+*p7pO<38D%BwtsW!I!Qyd;V5Ub+6h5Kbc zj&c^! zs#xJ_)#E;@PMc_Tc@{@we0+Sku{t347t7%OV=U6=TBNbtB0U$7zu|L%MXJH9l1Evk zX9SB>8~o#Sc&OL>FV;F)FeiV$icvSOUu1E(+QKIF%_fc4Hfc<^>0>^P`$=XUYn;1R zYK;1$PND^$Nt919>RkT>3yz%wPQ|FVS8unVpFSwsQ*F9_jb&@=;uh(14WBpfg+;1o zD1Y%e$Rgdle>|_a_*iw)^Q;!EFQw12N!Q;d-IpZkekGZ8xPELqJFIE(*$AJJupZN& zv5fO_Iv%S!uP$Q2`&&()H>j(GTCfh5J~v9bw*Odv%JC^3=Qsb9J}XJ;Ly~lDlca0@ z+uzE^msz?OHd&WhI`;#3Eofc{ndXlE?@V(|sLM=qQ>e>Kb6Kd%OmknT%S>}+sLM=q zYpBaib8)E4Omlar%S>~9sLM=qgQ&|)bBUKFBP;>ycT0HzYI7h2rsLmft{Zjwan8r|S zFS9&8%1mP=wwGCcK9!lCRk6LyG{1qm%ruvay3DeV$V_v^QhUQ^X>J*HnaOi%>3bL8 zz8uP}MSzTZap?Pu8I1dQxO8Y>Fz%Uv`=?xPMNj83hc+{cv#Ky!`qeWw^h`mA#n-GVbHC(Lcms z+z({#8drnO{%~w#o|nO9e+)MJ!?2CnAA`;Q7;N^(Xm9q%V6#7I`{rSv@m#3S$kgY2 z`aYA?XJqO#GW8jm`ix9{My5U^Q=gHk&&bqgWa=|A^%*(6&%gG1#rqzn?>|ZR9Xb7e z{o3~u?>p-0_v_dGmcQ=%uYJ|@q)EK9{uR(E?cpuQn@01HWZ11I7dvEFPu$F=z{yEN8+dwN+K_ieD0H>%%2-`nul zzEZ7nVV{5PJ(c-PxWVbMm(~X(hH?K8y*$_()*o+N>gZ=&3lyG;0&Db!w z^O7)jZTUMR7jQoueV;&suUCp--EF4~PCW^%XxM6lZTV`ls9yaI_8VNERqPXNaBGLg ztn}5z3icWI@QL4W(O}x=2ld2k(Z=2j$Yy&@YHu*zU)0Uxb7b>#4L18@FvT{uq1Z+? z`){-{&&y!*d<~`?!S?3$5y+HN$maDlbn}`UOu2?_%zI?$<~=glyr%}6F<`J68-~A8 zoM2v4oFHSI9MtzeK~9hDe2n4*b&3;YiW6jt6J&}LWQr4HiW6jt6J&}LWQr4HiW6ko zHwl^I1exLlnc@VQ;slxE1exLlnc@VQ;zVk1>@RAzH<;oC+fbY!Q=A}EoFG%2AXA(m zQ=A}EoFG%2AXA(mQ=A}EoFG%2AXA(mQ=A}EoFG%2AXA(mQ=A}EoM4Q3ZvCq4zE#YK zAKXLf!NI}?FLQKLT!$AixOHr8?bDz64GtY_&+AqRF&Ot#!m$jGlW|^EuKR<*UDl1| zr8apPjC(8X_s(Q6?x*A$VQ27%6|4DQ&3|#rt%6TDbjQe@Z!2%|JPQ*I#=Ya_w7FvN zu}Uet=fYVA)3|`2YuV!SlX`&CdeV&Hfl{_S0aq{|1}qWw3ef4FA?VcZ1DqWw3dz3^uQ=!REC! z*u4G*oA=9L^Bx&&-aCWM`)kw&blfLrjyg;UWL8d%Hru;&tx)hmm5SeOk zWU6D4DSweEf01cFOk^|PBc=A5)ZSq77Szdqkja;j$*+*f$B@bEkjd+i$s3W$8J&4`6idhyZ^#sr$P|;v6t~C}&&U+>$Q1L) zlo!a9FUXWf$dq5mgmsSmDA!4b&5A+ice&UPh^T=WQuKMig{$p1!T$*WXc_6$|+>ZL1fBJ zWXf6O^yj-@zbl0E0H~)w-|6RN&I3s8^=G=Y_6Dav-=!U2A79cw*I@D})YG5u(&n9v zHsop8hP)1$JP>(y8!y!uipP(zXhPrR~u|zbA!{* z>(@T~_zaHi)6c7}bS}U4gTa)BR1qCg!}T)LucRng==E zKhr)JP*3;UwE1vDr2*ok8x>p=7yf0r+Q49^E}v}^t?|04GYw%Hb74IjkNa0HB9%Mx{}YN z<&JS3(mhK5jg?<>Q-+@IZ)y2r=;_{=mOF-??vrWxVw_jHucqaQp{ILtTD};1y0@q0 zi=n6cd|JL3b3rt|<9rm=ocR1mj5^f}s1u`3V~X^=`P;k`>h!tTp2io{iBYGq26bZ8 zX{mLfwop zV-AYqO=@q7Q9PqgJ}mWzqfS07_21BGd_kQU+mk1wPK-K@JE#+*PGb$~ z#HiD_gE}$lcptic!WAJ>g3_56QfQZjykb%9Mi*%&iSobSGv~~F7um2R`p+*3@AHX%^YzS~Gv}Ur z?z!jQodItcyybY{4THCM4&E?$ThD?w4Bp~Ac*EdroeSPDc#HGk8C%S!Yo#u2ah}?j znzQB7;0@zii~ryagSVIu-Y|HJ`QQzMx0ny!FnC)dfHw@@Vh&=j>5TlubOWDaUKjA3 z#cKzir;^-T;;QM1_DwgmpJHCi@H~~|;SxVnos&FVc#D&0-(n-$PjQlW#qGrVEDk5R zneY}T!CP!ZJ1I``J(0IK33-c+kWX=vPl~+7NyuAlgnWvVyj1LFaT4+t8zG-!&eeJD z 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

5Tbmc1+4d39nQfmEo!Rz5(V1A>k~w0wmu{k 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

B`sGcY`#G-GRg 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ː?DP6wv?*ine~0A<;Kcju8zI+sg!P=To(s!G37O=h-tnUbaZQuQ%q=z1PL^LnUgD90V4%%y!PEOMb>dqaj(11Q_dx!I<&+v*{s&gf3oDNYC;HNg zmH3W|kKjbR^T?$U*D`*8<)FAv%D**Si*8{p5OrT~eizngRk!b6v}RZ*)53iC>9fKb z82v8w8}i{{`S7rOc=&dUk-xHBD16NNgKAOa!y}gu56g##<-^1B;bHmkuzYw}K0GWR z9+nRe%ZG>M!^867VfpZ|e0W$sJS+iw%WkL*b{a zW~s)Ruuf0*6uE3DEE@{TF2G04FQu*zmJNl6Se%<=xh7cl6qY@OWl!Nde@~9x>%tl{ z*;C}Qq41uSsnN!kPljbr;bJY#p2ln2WKWUHp2D)Hu?tf83d^>_ zvZt`@DJ**m%Z9?L|Ab#6$CVt`m8o77`D0a*eC`^qY`NvTEDnOR<8$$_Y7}AB8NyB8 z33K4Sjn%Py7S}D(3FsE)YN>ydz9RnytJV~ju7J@w%NsOW8T#ShSk3LFw)q_EiYs2x~9p4`ERi7J}iF#md^moXMp8b!16s{`5v(R6Igx=EPn=;p99M;g5?Xr@{wTqSJYCf zjub|Rvt|U=IyIQMi8UIq^gk^94@3LXs9+sYmrRQPkd06%lmd=OAna

G^}%; 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∋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

9{r*Z2rN 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%0DTQVfZN&bU4cvJIK1Q?4_rpyz{}n@ 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%0DTwZ!`>c1)ZY*yrtbZbk} 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⁣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 Date: Wed, 29 Nov 2017 12:20:09 -0500 Subject: [PATCH 391/490] Fix node names sharing same name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Uses node name if it is set, and globally unique id otherwise. This may still break in some models (glTF2 spec doesn’t guaruntee name values to be unique). However, I couldn’t cause it to break any further using gltf2 models on hand. Closes #1600 --- code/glTF2Importer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/glTF2Importer.cpp b/code/glTF2Importer.cpp index ca12d3657..94e5ca3f1 100644 --- a/code/glTF2Importer.cpp +++ b/code/glTF2Importer.cpp @@ -516,7 +516,9 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector& { Node& node = *ptr; - aiNode* ainode = new aiNode(node.name); + std::string nameOrId = node.name.empty() ? node.id : node.name; + + aiNode* ainode = new aiNode(nameOrId); if (!node.children.empty()) { ainode->mNumChildren = unsigned(node.children.size()); From 46caecdf350c1de628aec028ae4c955ab151f987 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 29 Nov 2017 21:02:52 +0100 Subject: [PATCH 392/490] enable compiler switch test for no exporter requested. --- code/D3MFExporter.cpp | 8 +++++--- code/D3MFExporter.h | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index 74fa7a6b4..70c2dd409 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -38,6 +38,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ +#ifndef ASSIMP_BUILD_NO_EXPORT +#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER + #include "D3MFExporter.h" #include @@ -69,8 +72,6 @@ void ExportScene3MF( const char* pFile, IOSystem* pIOSystem, const aiScene* pSce namespace D3MF { -#ifndef ASSIMP_BUILD_NO3MF_EXPORTER - D3MFExporter::D3MFExporter( const char* pFile, const aiScene* pScene ) : mArchiveName( pFile ) , m_zipArchive( nullptr ) @@ -276,8 +277,9 @@ void D3MFExporter::writeRelInfoToFile( const std::string &folder, const std::str zip_entry_close( m_zipArchive ); } -#endif // ASSIMP_BUILD_NO3MF_EXPORTER } // Namespace D3MF } // Namespace Assimp +#endif // ASSIMP_BUILD_NO3MF_EXPORTER +#endif // ASSIMP_BUILD_NO_EXPORT diff --git a/code/D3MFExporter.h b/code/D3MFExporter.h index 862e55fbd..8a38eff05 100644 --- a/code/D3MFExporter.h +++ b/code/D3MFExporter.h @@ -58,6 +58,7 @@ class IOStream; namespace D3MF { +#ifndef ASSIMP_BUILD_NO_EXPORT #ifndef ASSIMP_BUILD_NO3MF_EXPORTER struct OpcPackageRelationship; @@ -92,6 +93,7 @@ private: }; #endif // ASSIMP_BUILD_NO3MF_EXPORTER +#endif // ASSIMP_BUILD_NO_EXPORT } // Namespace D3MF } // Namespace Assimp From ac23034816c1921b364edbaad97187186858ac8e Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 30 Nov 2017 00:32:37 +0100 Subject: [PATCH 393/490] fix the exporter unittest when the export is disabled. --- test/unit/utD3MFImportExport.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/unit/utD3MFImportExport.cpp b/test/unit/utD3MFImportExport.cpp index efc87c786..d344f0a3f 100644 --- a/test/unit/utD3MFImportExport.cpp +++ b/test/unit/utD3MFImportExport.cpp @@ -63,6 +63,8 @@ public: return ( nullptr != scene ); } +#ifndef ASSIMP_BUILD_NO_EXPORT + virtual bool exporterTest() { Assimp::Importer importer; const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/3MF/box.3mf", 0 ); @@ -70,13 +72,17 @@ public: Assimp::Exporter exporter; return AI_SUCCESS == exporter.Export( scene, "3mf", "test.3mf" ); } - +#endif // ASSIMP_BUILD_NO_EXPORT }; TEST_F(utD3MFImporterExporter, import3MFFromFileTest) { EXPECT_TRUE(importerTest()); } +#ifndef ASSIMP_BUILD_NO_EXPORT + TEST_F( utD3MFImporterExporter, export3MFtoMemTest ) { EXPECT_TRUE( exporterTest() ); } + +#endif // ASSIMP_BUILD_NO_EXPORT From 915ee95ad3d7ea7679d5ba61881fe113faf0af0a Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 30 Nov 2017 09:12:41 +0100 Subject: [PATCH 394/490] Fx nullptr dereference. --- contrib/zip/src/miniz.h | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/zip/src/miniz.h b/contrib/zip/src/miniz.h index 935ee3d5f..56c7e8184 100644 --- a/contrib/zip/src/miniz.h +++ b/contrib/zip/src/miniz.h @@ -3030,6 +3030,7 @@ static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_ static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) { + if (pElements==NULL) return MZ_FALSE; size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); return MZ_TRUE; From 5822d1920e83b08be6cf774e08affe5da967f880 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 30 Nov 2017 09:22:55 +0100 Subject: [PATCH 395/490] Update STLLoader.cpp Retrigger travis + appveyor. --- code/STLLoader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index 96a81c22f..be4c7584d 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -58,6 +58,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; namespace { + static const aiImporterDesc desc = { "Stereolithography (STL) Importer", "", From 2897433358a9d2fe8294e66044fb3f7a6f34dc79 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 30 Nov 2017 10:47:52 +0100 Subject: [PATCH 396/490] Update miniz.h Add an assertion test and return true to not change the expected behaviour of buggy function. --- contrib/zip/src/miniz.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/zip/src/miniz.h b/contrib/zip/src/miniz.h index 56c7e8184..f48eb74ee 100644 --- a/contrib/zip/src/miniz.h +++ b/contrib/zip/src/miniz.h @@ -3030,7 +3030,8 @@ static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_ static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) { - if (pElements==NULL) return MZ_FALSE; + assert(NULL != pElements); + if (pElements==NULL) return MZ_TRUE; size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); return MZ_TRUE; From 42c1c733b93b0807362ba34eedd12c28f6333d95 Mon Sep 17 00:00:00 2001 From: Giuseppe Barbieri Date: Fri, 1 Dec 2017 15:20:50 +0100 Subject: [PATCH 397/490] Update MD3Loader.cpp --- code/MD3Loader.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/MD3Loader.cpp b/code/MD3Loader.cpp index f6a3a85dd..11a6e56dd 100644 --- a/code/MD3Loader.cpp +++ b/code/MD3Loader.cpp @@ -1018,11 +1018,11 @@ void MD3Importer::InternReadFile( const std::string& pFile, // Convert the normal vector to uncompressed float3 format aiVector3D& nor = pcMesh->mNormals[iCurrent]; - LatLngNormalToVec3(pcVertices[pcTriangles->INDEXES[c]].NORMAL,(ai_real*)&nor); + LatLngNormalToVec3(pcVertices[index].NORMAL,(ai_real*)&nor); // Read texture coordinates - pcMesh->mTextureCoords[0][iCurrent].x = pcUVs[ pcTriangles->INDEXES[c]].U; - pcMesh->mTextureCoords[0][iCurrent].y = 1.0f-pcUVs[ pcTriangles->INDEXES[c]].V; + pcMesh->mTextureCoords[0][iCurrent].x = pcUVs[index].U; + pcMesh->mTextureCoords[0][iCurrent].y = 1.0f-pcUVs[index].V; } // Flip face order if necessary if (!shader || shader->cull == Q3Shader::CULL_CW) { From af8e297e0fc7c339aa5a3c05aeb1aa8ec5e68c00 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 2 Dec 2017 17:10:06 +0200 Subject: [PATCH 398/490] BaseImporter: Replace ScopeGuard with std::unique_ptr --- code/BaseImporter.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/code/BaseImporter.cpp b/code/BaseImporter.cpp index 09b32e3ae..b9b9eeb71 100644 --- a/code/BaseImporter.cpp +++ b/code/BaseImporter.cpp @@ -89,12 +89,12 @@ aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, FileSystemFilter filter(pFile,pIOHandler); // create a scene object to hold the data - ScopeGuard sc(new aiScene()); + std::unique_ptr sc(new aiScene()); // dispatch importing try { - InternReadFile( pFile, sc, &filter); + InternReadFile( pFile, sc.get(), &filter); } catch( const std::exception& err ) { // extract error description @@ -104,8 +104,7 @@ aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, } // return what we gathered from the import. - sc.dismiss(); - return sc; + return sc.release(); } // ------------------------------------------------------------------------------------------------ From f35d5952dc27c78b84d87fa0c4060e506f3bf091 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 2 Dec 2017 17:13:43 +0200 Subject: [PATCH 399/490] BlenderLoader: Replace ScopeGuard with std::unique_ptr --- code/BlenderLoader.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/code/BlenderLoader.cpp b/code/BlenderLoader.cpp index 6d4c97cbf..824954f56 100644 --- a/code/BlenderLoader.cpp +++ b/code/BlenderLoader.cpp @@ -1148,7 +1148,7 @@ void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, co // ------------------------------------------------------------------------------------------------ aiCamera* BlenderImporter::ConvertCamera(const Scene& /*in*/, const Object* obj, const Camera* cam, ConversionData& /*conv_data*/) { - ScopeGuard out(new aiCamera()); + std::unique_ptr out(new aiCamera()); out->mName = obj->id.name+2; out->mPosition = aiVector3D(0.f, 0.f, 0.f); out->mUp = aiVector3D(0.f, 1.f, 0.f); @@ -1159,13 +1159,13 @@ aiCamera* BlenderImporter::ConvertCamera(const Scene& /*in*/, const Object* obj, out->mClipPlaneNear = cam->clipsta; out->mClipPlaneFar = cam->clipend; - return out.dismiss(); + return out.release(); } // ------------------------------------------------------------------------------------------------ aiLight* BlenderImporter::ConvertLight(const Scene& /*in*/, const Object* obj, const Lamp* lamp, ConversionData& /*conv_data*/) { - ScopeGuard out(new aiLight()); + std::unique_ptr out(new aiLight()); out->mName = obj->id.name+2; switch (lamp->type) @@ -1203,7 +1203,7 @@ aiLight* BlenderImporter::ConvertLight(const Scene& /*in*/, const Object* obj, c out->mColorAmbient = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy; out->mColorSpecular = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy; out->mColorDiffuse = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy; - return out.dismiss(); + return out.release(); } // ------------------------------------------------------------------------------------------------ @@ -1221,7 +1221,7 @@ aiNode* BlenderImporter::ConvertNode(const Scene& in, const Object* obj, Convers ++it; } - ScopeGuard node(new aiNode(obj->id.name+2)); // skip over the name prefix 'OB' + std::unique_ptr node(new aiNode(obj->id.name+2)); // skip over the name prefix 'OB' if (obj->data) { switch (obj->type) { @@ -1305,14 +1305,14 @@ aiNode* BlenderImporter::ConvertNode(const Scene& in, const Object* obj, Convers aiNode** nd = node->mChildren = new aiNode*[node->mNumChildren](); for (const Object* nobj :children) { *nd = ConvertNode(in,nobj,conv_data,node->mTransformation * parentTransform); - (*nd++)->mParent = node; + (*nd++)->mParent = node.get(); } } // apply modifiers modifier_cache->ApplyModifiers(*node,conv_data,in,*obj); - return node.dismiss(); + return node.release(); } #endif // ASSIMP_BUILD_NO_BLEND_IMPORTER From 6f50be82aac12d1edb72b127ee9f6b4c1eda57d4 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 2 Dec 2017 17:15:57 +0200 Subject: [PATCH 400/490] 3MF: Replace ScopeGuard with std::unique_ptr --- code/D3MFImporter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index 2fcfefa6d..3d1846d21 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -106,7 +106,7 @@ public: private: aiNode* ReadObject(aiScene* scene) { - ScopeGuard node(new aiNode()); + std::unique_ptr node(new aiNode()); std::vector meshIds; @@ -146,7 +146,7 @@ private: std::copy(meshIds.begin(), meshIds.end(), node->mMeshes); - return node.dismiss(); + return node.release(); } From e8eccfa27da7d6be568e4e3773d61c9a2e9ea6a2 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 2 Dec 2017 17:19:18 +0200 Subject: [PATCH 401/490] FBX: Replace ScopeGuard with std::unique_ptr --- code/FBXConverter.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index a5e44a607..ea911b270 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -2744,10 +2744,10 @@ aiNodeAnim* Converter::GenerateRotationNodeAnim( const std::string& name, double& max_time, double& min_time ) { - ScopeGuard na( new aiNodeAnim() ); + std::unique_ptr na( new aiNodeAnim() ); na->mNodeName.Set( name ); - ConvertRotationKeys( na, curves, layer_map, start, stop, max_time, min_time, target.RotationOrder() ); + ConvertRotationKeys( na.get(), curves, layer_map, start, stop, max_time, min_time, target.RotationOrder() ); // dummy scaling key na->mScalingKeys = new aiVectorKey[ 1 ]; @@ -2763,7 +2763,7 @@ aiNodeAnim* Converter::GenerateRotationNodeAnim( const std::string& name, na->mPositionKeys[ 0 ].mTime = 0.; na->mPositionKeys[ 0 ].mValue = aiVector3D(); - return na.dismiss(); + return na.release(); } aiNodeAnim* Converter::GenerateScalingNodeAnim( const std::string& name, @@ -2774,10 +2774,10 @@ aiNodeAnim* Converter::GenerateScalingNodeAnim( const std::string& name, double& max_time, double& min_time ) { - ScopeGuard na( new aiNodeAnim() ); + std::unique_ptr na( new aiNodeAnim() ); na->mNodeName.Set( name ); - ConvertScaleKeys( na, curves, layer_map, start, stop, max_time, min_time ); + ConvertScaleKeys( na.get(), curves, layer_map, start, stop, max_time, min_time ); // dummy rotation key na->mRotationKeys = new aiQuatKey[ 1 ]; @@ -2793,7 +2793,7 @@ aiNodeAnim* Converter::GenerateScalingNodeAnim( const std::string& name, na->mPositionKeys[ 0 ].mTime = 0.; na->mPositionKeys[ 0 ].mValue = aiVector3D(); - return na.dismiss(); + return na.release(); } @@ -2806,10 +2806,10 @@ aiNodeAnim* Converter::GenerateTranslationNodeAnim( const std::string& name, double& min_time, bool inverse ) { - ScopeGuard na( new aiNodeAnim() ); + std::unique_ptr na( new aiNodeAnim() ); na->mNodeName.Set( name ); - ConvertTranslationKeys( na, curves, layer_map, start, stop, max_time, min_time ); + ConvertTranslationKeys( na.get(), curves, layer_map, start, stop, max_time, min_time ); if ( inverse ) { for ( unsigned int i = 0; i < na->mNumPositionKeys; ++i ) { @@ -2831,7 +2831,7 @@ aiNodeAnim* Converter::GenerateTranslationNodeAnim( const std::string& name, na->mRotationKeys[ 0 ].mTime = 0.; na->mRotationKeys[ 0 ].mValue = aiQuaternion(); - return na.dismiss(); + return na.release(); } aiNodeAnim* Converter::GenerateSimpleNodeAnim( const std::string& name, @@ -2845,7 +2845,7 @@ aiNodeAnim* Converter::GenerateSimpleNodeAnim( const std::string& name, bool reverse_order ) { - ScopeGuard na( new aiNodeAnim() ); + std::unique_ptr na( new aiNodeAnim() ); na->mNodeName.Set( name ); const PropertyTable& props = target.Props(); @@ -2917,7 +2917,7 @@ aiNodeAnim* Converter::GenerateSimpleNodeAnim( const std::string& name, // which requires all of rotation, scaling and translation // to be set. if ( chain[ TransformationComp_Scaling ] != iter_end ) { - ConvertScaleKeys( na, ( *chain[ TransformationComp_Scaling ] ).second, + ConvertScaleKeys( na.get(), ( *chain[ TransformationComp_Scaling ] ).second, layer_map, start, stop, max_time, @@ -2933,7 +2933,7 @@ aiNodeAnim* Converter::GenerateSimpleNodeAnim( const std::string& name, } if ( chain[ TransformationComp_Rotation ] != iter_end ) { - ConvertRotationKeys( na, ( *chain[ TransformationComp_Rotation ] ).second, + ConvertRotationKeys( na.get(), ( *chain[ TransformationComp_Rotation ] ).second, layer_map, start, stop, max_time, @@ -2951,7 +2951,7 @@ aiNodeAnim* Converter::GenerateSimpleNodeAnim( const std::string& name, } if ( chain[ TransformationComp_Translation ] != iter_end ) { - ConvertTranslationKeys( na, ( *chain[ TransformationComp_Translation ] ).second, + ConvertTranslationKeys( na.get(), ( *chain[ TransformationComp_Translation ] ).second, layer_map, start, stop, max_time, @@ -2967,7 +2967,7 @@ aiNodeAnim* Converter::GenerateSimpleNodeAnim( const std::string& name, } } - return na.dismiss(); + return na.release(); } Converter::KeyFrameListList Converter::GetKeyframeList( const std::vector& nodes, int64_t start, int64_t stop ) From bd4f0245626a7466efdd35a5750dba224c8e51fe Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 2 Dec 2017 17:22:19 +0200 Subject: [PATCH 402/490] XGLLoader: Replace ScopeGuard with std::unique_ptr --- code/XGLLoader.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/code/XGLLoader.cpp b/code/XGLLoader.cpp index df33229a6..8ef91afac 100644 --- a/code/XGLLoader.cpp +++ b/code/XGLLoader.cpp @@ -359,7 +359,7 @@ void XGLImporter::ReadLighting(TempScope& scope) // ------------------------------------------------------------------------------------------------ aiLight* XGLImporter::ReadDirectionalLight() { - ScopeGuard l(new aiLight()); + std::unique_ptr l(new aiLight()); l->mType = aiLightSource_DIRECTIONAL; while (ReadElementUpToClosing("directionallight")) { @@ -374,13 +374,13 @@ aiLight* XGLImporter::ReadDirectionalLight() l->mColorSpecular = ReadCol3(); } } - return l.dismiss(); + return l.release(); } // ------------------------------------------------------------------------------------------------ aiNode* XGLImporter::ReadObject(TempScope& scope, bool skipFirst, const char* closetag) { - ScopeGuard nd(new aiNode()); + std::unique_ptr nd(new aiNode()); std::vector children; std::vector meshes; @@ -463,11 +463,11 @@ aiNode* XGLImporter::ReadObject(TempScope& scope, bool skipFirst, const char* cl nd->mChildren = new aiNode*[nd->mNumChildren](); for(unsigned int i = 0; i < nd->mNumChildren; ++i) { nd->mChildren[i] = children[i]; - children[i]->mParent = nd; + children[i]->mParent = nd.get(); } } - return nd.dismiss(); + return nd.release(); } // ------------------------------------------------------------------------------------------------ @@ -539,7 +539,7 @@ aiMatrix4x4 XGLImporter::ReadTrafo() // ------------------------------------------------------------------------------------------------ aiMesh* XGLImporter::ToOutputMesh(const TempMaterialMesh& m) { - ScopeGuard mesh(new aiMesh()); + std::unique_ptr mesh(new aiMesh()); mesh->mNumVertices = static_cast(m.positions.size()); mesh->mVertices = new aiVector3D[mesh->mNumVertices]; @@ -576,7 +576,7 @@ aiMesh* XGLImporter::ToOutputMesh(const TempMaterialMesh& m) mesh->mPrimitiveTypes = m.pflags; mesh->mMaterialIndex = m.matid; - return mesh.dismiss(); + return mesh.release(); } // ------------------------------------------------------------------------------------------------ @@ -745,7 +745,7 @@ void XGLImporter::ReadMaterial(TempScope& scope) { const unsigned int mat_id = ReadIDAttr(); - ScopeGuard mat(new aiMaterial()); + std::unique_ptr mat(new aiMaterial()); while (ReadElementUpToClosing("mat")) { const std::string& s = GetElementName(); if (s == "amb") { @@ -774,8 +774,8 @@ void XGLImporter::ReadMaterial(TempScope& scope) } } - scope.materials[mat_id] = mat; - scope.materials_linear.push_back(mat.dismiss()); + scope.materials[mat_id] = mat.get(); + scope.materials_linear.push_back(mat.release()); } From b60d84a8a27a140e7a6ce4eb8b38aa6eab264ca5 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 2 Dec 2017 17:23:49 +0200 Subject: [PATCH 403/490] C4D: Replace ScopeGuard with std::unique_ptr --- code/C4DImporter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/C4DImporter.cpp b/code/C4DImporter.cpp index 39514f6b8..1344f32fb 100644 --- a/code/C4DImporter.cpp +++ b/code/C4DImporter.cpp @@ -185,11 +185,11 @@ void C4DImporter::InternReadFile( const std::string& pFile, if(mesh->mMaterialIndex >= mat_count) { ++mat_count; - ScopeGuard def_material(new aiMaterial()); + std::unique_ptr def_material(new aiMaterial()); const aiString name(AI_DEFAULT_MATERIAL_NAME); def_material->AddProperty(&name, AI_MATKEY_NAME); - materials.push_back(def_material.dismiss()); + materials.push_back(def_material.release()); break; } } @@ -412,7 +412,7 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object) const CPolygon* polys = polyObject->GetPolygonR(); ai_assert(polys != NULL); - ScopeGuard mesh(new aiMesh()); + std::unique_ptr mesh(new aiMesh()); mesh->mNumFaces = static_cast(polyCount); aiFace* face = mesh->mFaces = new aiFace[mesh->mNumFaces](); @@ -616,7 +616,7 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object) } mesh->mMaterialIndex = ResolveMaterial(polyObject); - return mesh.dismiss(); + return mesh.release(); } From 2c3558fdd04dd0242fae58a0a43a517617448a77 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 2 Dec 2017 17:24:19 +0200 Subject: [PATCH 404/490] Remove ScopeGuard --- code/BaseImporter.h | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/code/BaseImporter.h b/code/BaseImporter.h index cee54c1ad..b424d2f83 100644 --- a/code/BaseImporter.h +++ b/code/BaseImporter.h @@ -65,42 +65,6 @@ class IOStream; #define AI_MAKE_MAGIC(string) ((uint32_t)((string[0] << 24) + \ (string[1] << 16) + (string[2] << 8) + string[3])) -// --------------------------------------------------------------------------- -template -struct ScopeGuard -{ - explicit ScopeGuard(T* obj) : obj(obj), mdismiss() {} - ~ScopeGuard () throw() { - if (!mdismiss) { - delete obj; - } - obj = NULL; - } - - T* dismiss() { - mdismiss=true; - return obj; - } - - operator T*() { - return obj; - } - - T* operator -> () { - return obj; - } - -private: - // no copying allowed. - ScopeGuard(); - ScopeGuard( const ScopeGuard & ); - ScopeGuard &operator = ( const ScopeGuard & ); - - T* obj; - bool mdismiss; -}; - - // --------------------------------------------------------------------------- /** FOR IMPORTER PLUGINS ONLY: The BaseImporter defines a common interface From 26f749fcd28c43e2d6df83c8ef65d18fa6e702de Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 2 Dec 2017 17:58:33 +0200 Subject: [PATCH 405/490] Re-enable Clang static analysis --- .travis.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.sh b/.travis.sh index 397e75077..fb42bd40d 100755 --- a/.travis.sh +++ b/.travis.sh @@ -46,7 +46,7 @@ if [ "$TRAVIS_OS_NAME" = "linux" ]; then if [ $ANALYZE = "ON" ] ; then if [ "$CC" = "clang" ]; then scan-build cmake -G "Unix Makefiles" -DBUILD_SHARED_LIBS=OFF -DASSIMP_BUILD_TESTS=OFF - scan-build --status-bugs make -j2 -v + scan-build --status-bugs make -j2 else cppcheck --version generate \ From 45f2f31011918dea3e558729c8ea5f88c1a214d3 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 2 Dec 2017 17:59:04 +0200 Subject: [PATCH 406/490] miniz: Remove some dead assignments --- contrib/zip/src/miniz.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/contrib/zip/src/miniz.h b/contrib/zip/src/miniz.h index 56c7e8184..841e9e128 100644 --- a/contrib/zip/src/miniz.h +++ b/contrib/zip/src/miniz.h @@ -3804,9 +3804,7 @@ mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_ind status = TINFL_STATUS_FAILED; else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); - cur_file_ofs += file_stat.m_comp_size; out_buf_ofs += file_stat.m_comp_size; - comp_remaining = 0; } else { @@ -4685,7 +4683,6 @@ mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive * return MZ_FALSE; } - cur_src_file_ofs += n; cur_dst_file_ofs += n; } pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); From e741e7aec6fe479d6eb638c08564bcee707e3633 Mon Sep 17 00:00:00 2001 From: Giuseppe Barbieri Date: Sat, 2 Dec 2017 23:16:10 +0100 Subject: [PATCH 407/490] Update MD5Loader.h --- code/MD5Loader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/MD5Loader.h b/code/MD5Loader.h index 9dfc08226..a68655f55 100644 --- a/code/MD5Loader.h +++ b/code/MD5Loader.h @@ -144,7 +144,7 @@ protected: // ------------------------------------------------------------------- /** Load the contents of a specific file into memory and - * alocates a buffer to keep it. + * allocates a buffer to keep it. * * mBuffer is modified to point to this buffer. * @param pFile File stream to be read From b92c3ca161995957759110c3d4b3e10761c0ba5e Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 4 Dec 2017 13:19:34 +0100 Subject: [PATCH 408/490] fix invalid op --- contrib/zip/src/miniz.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/contrib/zip/src/miniz.h b/contrib/zip/src/miniz.h index f48eb74ee..8428e6ce0 100644 --- a/contrib/zip/src/miniz.h +++ b/contrib/zip/src/miniz.h @@ -3030,8 +3030,10 @@ static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_ static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) { - assert(NULL != pElements); - if (pElements==NULL) return MZ_TRUE; +// assert(NULL != pElements); + if (pElements == NULL) { + return MZ_TRUE; + } size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); return MZ_TRUE; From fe6608175b5b965b9d6c2ec3ac72d733cf9244d8 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 4 Dec 2017 15:19:34 +0100 Subject: [PATCH 409/490] Update miniz.h Fix test against null bytes to push back. --- contrib/zip/src/miniz.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/contrib/zip/src/miniz.h b/contrib/zip/src/miniz.h index b8e4cf230..b0db0005b 100644 --- a/contrib/zip/src/miniz.h +++ b/contrib/zip/src/miniz.h @@ -3030,10 +3030,8 @@ static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_ static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) { -// assert(NULL != pElements); - if (pElements == NULL) { - return MZ_TRUE; - } + if (n==0) return MZ_TRUE; + assert(NULL != pElements); size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); return MZ_TRUE; From 36cddcc8b758fc4ebb6a5b81a2c08a8bf034fdb2 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 4 Dec 2017 15:20:23 +0100 Subject: [PATCH 410/490] Update miniz.h retrigger CI. --- contrib/zip/src/miniz.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/zip/src/miniz.h b/contrib/zip/src/miniz.h index b0db0005b..916fb1ff8 100644 --- a/contrib/zip/src/miniz.h +++ b/contrib/zip/src/miniz.h @@ -3031,7 +3031,7 @@ static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_ static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) { if (n==0) return MZ_TRUE; - assert(NULL != pElements); + assert(NULL!=pElements); size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); return MZ_TRUE; From 95e9cd75fac5d079bbfdae3be881d317c1fa3521 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 6 Dec 2017 21:41:48 +0100 Subject: [PATCH 411/490] 3MF: fix working test for 3MF-export. --- code/D3MFExporter.cpp | 60 +++++++++++++++++++++++++++++++------ code/D3MFExporter.h | 3 ++ code/D3MFImporter.cpp | 1 - code/D3MFOpcPackage.cpp | 8 ++--- include/assimp/IOSystem.hpp | 12 ++++++++ test/unit/utIOSystem.cpp | 3 ++ 6 files changed, 73 insertions(+), 14 deletions(-) diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index 70c2dd409..4fb13ca7b 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -63,6 +63,11 @@ void ExportScene3MF( const char* pFile, IOSystem* pIOSystem, const aiScene* pSce } D3MF::D3MFExporter myExporter( pFile, pScene ); if ( myExporter.validate() ) { + if ( pIOSystem->Exists( pFile ) ) { + if ( !pIOSystem->DeleteFile( pFile ) ) { + throw DeadlyExportError( "File exists, cannot override : " + std::string( pFile ) ); + } + } bool ok = myExporter.exportArchive(pFile); if ( !ok ) { throw DeadlyExportError( "Could not export 3MP archive: " + std::string( pFile ) ); @@ -76,6 +81,9 @@ D3MFExporter::D3MFExporter( const char* pFile, const aiScene* pScene ) : mArchiveName( pFile ) , m_zipArchive( nullptr ) , mScene( pScene ) +, mModelOutput() +, mRelOutput() +, mContentOutput() , mBuildItems() , mRelations() { // empty @@ -107,6 +115,7 @@ bool D3MFExporter::exportArchive( const char *file ) { if ( nullptr == m_zipArchive ) { return false; } + ok |= exportContentTypes(); ok |= export3DModel(); ok |= exportRelations(); @@ -116,16 +125,36 @@ bool D3MFExporter::exportArchive( const char *file ) { return ok; } + +bool D3MFExporter::exportContentTypes() { + mContentOutput.clear(); + + mContentOutput << ""; + mContentOutput << std::endl; + mContentOutput << ""; + mContentOutput << std::endl; + mContentOutput << ""; + mContentOutput << std::endl; + mContentOutput << ""; + mContentOutput << std::endl; + mContentOutput << ""; + mContentOutput << std::endl; + exportContentTyp( XmlTag::CONTENT_TYPES_ARCHIVE ); + + return true; +} + bool D3MFExporter::exportRelations() { mRelOutput.clear(); - mRelOutput << "\n"; - mRelOutput << "\n"; + mRelOutput << ""; + mRelOutput << std::endl; + mRelOutput << ""; for ( size_t i = 0; i < mRelations.size(); ++i ) { - mRelOutput << "target << "\" "; - mRelOutput << "id=\"" << mRelations[i]->id << "\" "; - mRelOutput << "Type=\"" << mRelations[ i ]->type << "/>"; + mRelOutput << "target << "\" "; + mRelOutput << "Id=\"" << mRelations[i]->id << "\" "; + mRelOutput << "Type=\"" << mRelations[ i ]->type << "\" />"; mRelOutput << std::endl; } mRelOutput << ""; @@ -157,12 +186,12 @@ bool D3MFExporter::export3DModel() { mModelOutput << "\n"; OpcPackageRelationship *info = new OpcPackageRelationship; - info->id = mArchiveName; - info->target = "rel0"; - info->type = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel"; + info->id = "rel0"; + info->target = "/3D/3DModel.model"; + info->type = XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE; mRelations.push_back( info ); - writeModelToArchive( "3D", "3DModel.model" ); + writeModelToArchive( "/3D", "3DModel.model" ); mModelOutput.flush(); return true; @@ -251,6 +280,19 @@ void D3MFExporter::writeBuild() { mModelOutput << std::endl; } +void D3MFExporter::exportContentTyp( const std::string &filename ) { + if ( nullptr == m_zipArchive ) { + throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." ); + } + const std::string entry = filename; + zip_entry_open( m_zipArchive, entry.c_str() ); + + const std::string &exportTxt( mContentOutput.str() ); + zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() ); + + zip_entry_close( m_zipArchive ); +} + void D3MFExporter::writeModelToArchive( const std::string &folder, const std::string &modelName ) { if ( nullptr == m_zipArchive ) { throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." ); diff --git a/code/D3MFExporter.h b/code/D3MFExporter.h index 8a38eff05..16efabf29 100644 --- a/code/D3MFExporter.h +++ b/code/D3MFExporter.h @@ -69,6 +69,7 @@ public: ~D3MFExporter(); bool validate(); bool exportArchive( const char *file ); + bool exportContentTypes(); bool exportRelations(); bool export3DModel(); @@ -79,6 +80,7 @@ protected: void writeVertex( const aiVector3D &pos ); void writeFaces( aiMesh *mesh ); void writeBuild(); + void exportContentTyp( const std::string &filename ); void writeModelToArchive( const std::string &folder, const std::string &modelName ); void writeRelInfoToFile( const std::string &folder, const std::string &relName ); @@ -88,6 +90,7 @@ private: const aiScene *mScene; std::ostringstream mModelOutput; std::ostringstream mRelOutput; + std::ostringstream mContentOutput; std::vector mBuildItems; std::vector mRelations; }; diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index 3d1846d21..fec36cebe 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -100,7 +100,6 @@ public: scene->mRootNode->mChildren = new aiNode*[scene->mRootNode->mNumChildren](); std::copy(children.begin(), children.end(), scene->mRootNode->mChildren); - } private: diff --git a/code/D3MFOpcPackage.cpp b/code/D3MFOpcPackage.cpp index 1db8cab13..20d4acaf6 100644 --- a/code/D3MFOpcPackage.cpp +++ b/code/D3MFOpcPackage.cpp @@ -416,9 +416,9 @@ public: void ParseChildNode(XmlReader* xmlReader) { OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship()); - relPtr->id = xmlReader->getAttributeValue(XmlTag::RELS_ATTRIB_ID.c_str()); - relPtr->type = xmlReader->getAttributeValue(XmlTag::RELS_ATTRIB_TYPE.c_str()); - relPtr->target = xmlReader->getAttributeValue(XmlTag::RELS_ATTRIB_TARGET.c_str()); + relPtr->id = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_ID.c_str()); + relPtr->type = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_TYPE.c_str()); + relPtr->target = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_TARGET.c_str()); m_relationShips.push_back(relPtr); } @@ -494,7 +494,7 @@ std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream* stream) { }); if(itr == reader.m_relationShips.end()) - throw DeadlyImportError("Cannot find" + XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE); + throw DeadlyImportError("Cannot find " + XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE); return (*itr)->target; } diff --git a/include/assimp/IOSystem.hpp b/include/assimp/IOSystem.hpp index 4a88c4a31..f4fbb6023 100644 --- a/include/assimp/IOSystem.hpp +++ b/include/assimp/IOSystem.hpp @@ -224,6 +224,8 @@ public: */ virtual bool ChangeDirectory( const std::string &path ); + virtual bool DeleteFile( const std::string &file ); + private: std::vector m_pathStack; }; @@ -342,6 +344,16 @@ bool IOSystem::ChangeDirectory( const std::string &path ) { #endif // _WIN32 } + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::DeleteFile( const std::string &file ) { + if ( file.empty() ) { + return false; + } + const int retCode( ::remove( file.c_str() ) ); + return ( 0 == retCode ); +} } //!ns Assimp #endif //AI_IOSYSTEM_H_INC diff --git a/test/unit/utIOSystem.cpp b/test/unit/utIOSystem.cpp index f33b22039..5e3e98031 100644 --- a/test/unit/utIOSystem.cpp +++ b/test/unit/utIOSystem.cpp @@ -73,3 +73,6 @@ TEST_F( IOSystemTest, accessDirectoryStackTest ) { EXPECT_EQ( 0U, pImp->StackSize() ); } +TEST_F( IOSystemTest, delFileTest ) { + EXPECT_FALSE( pImp->DeleteFile( "none" ) ); +} From c23c63e82119ecbead5da2cb86e8d5bb3d686aa1 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 6 Dec 2017 22:15:34 +0100 Subject: [PATCH 412/490] fix correct folder naming scheme. --- code/D3MFExporter.cpp | 2 +- code/D3MFOpcPackage.cpp | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index 4fb13ca7b..0320dac21 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -191,7 +191,7 @@ bool D3MFExporter::export3DModel() { info->type = XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE; mRelations.push_back( info ); - writeModelToArchive( "/3D", "3DModel.model" ); + writeModelToArchive( "3D", "3DModel.model" ); mModelOutput.flush(); return true; diff --git a/code/D3MFOpcPackage.cpp b/code/D3MFOpcPackage.cpp index 20d4acaf6..49143fee7 100644 --- a/code/D3MFOpcPackage.cpp +++ b/code/D3MFOpcPackage.cpp @@ -413,14 +413,22 @@ public: // empty } + bool validateRels( OpcPackageRelationshipPtr &relPtr ) { + if ( relPtr->id.empty() || relPtr->type.empty() || relPtr->target.empty() ) { + return false; + } + return true; + } + void ParseChildNode(XmlReader* xmlReader) { OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship()); relPtr->id = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_ID.c_str()); relPtr->type = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_TYPE.c_str()); relPtr->target = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_TARGET.c_str()); - - m_relationShips.push_back(relPtr); + if ( validateRels( relPtr ) ) { + m_relationShips.push_back( relPtr ); + } } std::vector m_relationShips; @@ -450,13 +458,19 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile) std::string rootFile = ReadPackageRootRelationship(fileStream); if ( rootFile.size() > 0 && rootFile[ 0 ] == '/' ) { rootFile = rootFile.substr( 1 ); + if ( rootFile[ 0 ] == '/' ) { + // deal with zipbug + rootFile = rootFile.substr( 1 ); + } } DefaultLogger::get()->debug(rootFile); mRootStream = mZipArchive->Open(rootFile.c_str()); - - ai_assert(mRootStream != nullptr); + ai_assert( mRootStream != nullptr ); + if ( nullptr == mRootStream ) { + throw DeadlyExportError( "Cannot open rootfile in archive : " + rootFile ); + } // const size_t size = zipArchive->FileSize(); // m_Data.resize( size ); From 0588d6cccf7d0d0a9d3e028143c23f5fb937e1b9 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 7 Dec 2017 09:44:48 +0100 Subject: [PATCH 413/490] FBX: closes https://github.com/assimp/assimp/issues/1619: return correct index for embedded textures. --- code/FBXConverter.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index ea911b270..24bdfdd11 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -440,12 +440,11 @@ private: bool FindTextureIndexByFilename(const Video& video, unsigned int& index) { index = 0; const char* videoFileName = video.FileName().c_str(); - for (auto texture = textures_converted.begin(); texture != textures_converted.end(); ++texture) - { + for (auto texture = textures_converted.begin(); texture != textures_converted.end(); ++texture) { if (!strcmp(texture->first->FileName().c_str(), videoFileName)) { + index = texture->second; return true; } - index++; } return false; } From 972d8517b5d5f0ceb3a95fd9b3acb1f121b8df64 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 7 Dec 2017 17:50:18 +0100 Subject: [PATCH 414/490] fix the model xml --- code/D3MFExporter.cpp | 5 +++-- code/D3MFImporter.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index 0320dac21..162d20764 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -240,13 +240,14 @@ void D3MFExporter::writeMesh( aiMesh *mesh ) { writeVertex( mesh->mVertices[ i ] ); } mModelOutput << "" << std::endl; - mModelOutput << "" << std::endl; writeFaces( mesh ); + + mModelOutput << "" << std::endl; } void D3MFExporter::writeVertex( const aiVector3D &pos ) { - mModelOutput << "<" << XmlTag::vertex << " x=\"" << pos.x << "\" y=\"" << pos.y << "\" z=\"" << pos.z << "\">"; + mModelOutput << "<" << XmlTag::vertex << " x=\"" << pos.x << "\" y=\"" << pos.y << "\" z=\"" << pos.z << "\" />"; mModelOutput << std::endl; } diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index fec36cebe..372416dbd 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -72,7 +72,7 @@ public: } ~XmlSerializer() { - + // empty } void ImportXml(aiScene* scene) { From 3e0b072263fd27b4fb709aea77ef2bf5182c0b67 Mon Sep 17 00:00:00 2001 From: Giuseppe Barbieri Date: Sat, 9 Dec 2017 23:42:23 +0100 Subject: [PATCH 415/490] Update FBXBinaryTokenizer.cpp --- code/FBXBinaryTokenizer.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/code/FBXBinaryTokenizer.cpp b/code/FBXBinaryTokenizer.cpp index 550859345..be73eaa95 100644 --- a/code/FBXBinaryTokenizer.cpp +++ b/code/FBXBinaryTokenizer.cpp @@ -435,11 +435,6 @@ void TokenizeBinary(TokenList& output_tokens, const char* input, unsigned int le TokenizeError("file is too short",0); } - if (strncmp(input,"Kaydara FBX Binary",18)) { - TokenizeError("magic bytes not found",0); - } - - //uint32_t offset = 0x15; const char* cursor = input + 0x15; From 0c2f4abe78be157645be34f617a1bd3b942390c0 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 10 Dec 2017 18:25:50 +0100 Subject: [PATCH 416/490] Update CREDITS Add list of patreons. --- CREDITS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CREDITS b/CREDITS index 43134ac03..df41c482d 100644 --- a/CREDITS +++ b/CREDITS @@ -158,3 +158,11 @@ Contributed X File exporter Contributed Step (stp) exporter For a more detailed list just check: https://github.com/assimp/assimp/network/members + +Patreons: +- migenius +- Marcus +- Cort +- elect +- Steffen + From 2f93cd77f8e74285886e0bb96476d8d2ba99bd12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B6ber?= Date: Mon, 11 Dec 2017 10:28:50 +0100 Subject: [PATCH 417/490] added path conversion code for unicode on windows --- code/DefaultIOStream.cpp | 3 +- code/DefaultIOSystem.cpp | 69 ++++++++++++++++++-------------- include/assimp/DefaultIOSystem.h | 6 --- 3 files changed, 42 insertions(+), 36 deletions(-) diff --git a/code/DefaultIOStream.cpp b/code/DefaultIOStream.cpp index cce9af19d..487ba27ca 100644 --- a/code/DefaultIOStream.cpp +++ b/code/DefaultIOStream.cpp @@ -123,7 +123,8 @@ size_t DefaultIOStream::FileSize() const // https://www.securecoding.cert.org/confluence/display/seccode/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+regular+file #if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) struct __stat64 fileStat; - int err = _stat64( mFilename.c_str(), &fileStat ); + //using fileno + fstat avoids having to handle the filename + int err = _fstat64( _fileno(mFile), &fileStat ); if (0 != err) return 0; mCachedSize = (size_t) (fileStat.st_size); diff --git a/code/DefaultIOSystem.cpp b/code/DefaultIOSystem.cpp index 8687b0059..fe4a219c0 100644 --- a/code/DefaultIOSystem.cpp +++ b/code/DefaultIOSystem.cpp @@ -54,31 +54,37 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #endif +#ifdef _WIN32 +#include +#endif + using namespace Assimp; -// ------------------------------------------------------------------------------------------------ -// Constructor. -DefaultIOSystem::DefaultIOSystem() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Destructor. -DefaultIOSystem::~DefaultIOSystem() -{ - // nothing to do here -} +// maximum path length +// XXX http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html +#ifdef PATH_MAX +# define PATHLIMIT PATH_MAX +#else +# define PATHLIMIT 4096 +#endif // ------------------------------------------------------------------------------------------------ // Tests for the existence of a file at the given path. bool DefaultIOSystem::Exists( const char* pFile) const { +#ifdef _WIN32 + wchar_t fileName16[PATHLIMIT]; + MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, pFile, -1, fileName16, PATHLIMIT); + struct _stat64 filestat; + if (0 != _wstat64(fileName16, &filestat)) + return false; +#else FILE* file = ::fopen( pFile, "rb"); if( !file) return false; ::fclose( file); +#endif return true; } @@ -88,10 +94,17 @@ IOStream* DefaultIOSystem::Open( const char* strFile, const char* strMode) { ai_assert(NULL != strFile); ai_assert(NULL != strMode); - - FILE* file = ::fopen( strFile, strMode); - if( NULL == file) - return NULL; + FILE* file; +#ifdef _WIN32 + wchar_t fileName16[PATHLIMIT]; + MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, strFile, -1, fileName16, PATHLIMIT); + std::string mode8(strMode); + file = ::_wfopen(fileName16, std::wstring(mode8.begin(), mode8.end()).c_str()); +#else + file = ::fopen(strFile, strMode); +#endif + if (nullptr == file) + return nullptr; return new DefaultIOStream(file, (std::string) strFile); } @@ -121,25 +134,23 @@ bool IOSystem::ComparePaths (const char* one, const char* second) const return !ASSIMP_stricmp(one,second); } -// maximum path length -// XXX http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html -#ifdef PATH_MAX -# define PATHLIMIT PATH_MAX -#else -# define PATHLIMIT 4096 -#endif - // ------------------------------------------------------------------------------------------------ // Convert a relative path into an absolute path -inline void MakeAbsolutePath (const char* in, char* _out) +inline static void MakeAbsolutePath (const char* in, char* _out) { ai_assert(in && _out); - char* ret; #if defined( _MSC_VER ) || defined( __MINGW32__ ) - ret = ::_fullpath( _out, in, PATHLIMIT ); + wchar_t out16[PATHLIMIT]; + wchar_t in16[PATHLIMIT]; + MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, in, -1, out16, PATHLIMIT); + wchar_t* ret = ::_wfullpath( out16, in16, PATHLIMIT ); + if (ret) + { + WideCharToMultiByte(CP_UTF8, MB_PRECOMPOSED, out16, -1, _out, PATHLIMIT, nullptr, nullptr); + } #else // use realpath - ret = realpath(in, _out); + char* ret = realpath(in, _out); #endif if(!ret) { // preserve the input path, maybe someone else is able to fix diff --git a/include/assimp/DefaultIOSystem.h b/include/assimp/DefaultIOSystem.h index d7cf031cf..718f5f4a5 100644 --- a/include/assimp/DefaultIOSystem.h +++ b/include/assimp/DefaultIOSystem.h @@ -52,12 +52,6 @@ namespace Assimp { class ASSIMP_API DefaultIOSystem : public IOSystem { public: - /** Constructor. */ - DefaultIOSystem(); - - /** Destructor. */ - ~DefaultIOSystem(); - // ------------------------------------------------------------------- /** Tests for the existence of a file at the given path. */ bool Exists( const char* pFile) const; From 28b01cbdd14c468c246ebea6b0275f870f402b62 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 11 Dec 2017 13:20:19 +0100 Subject: [PATCH 418/490] Update to 4.1.0 --- code/Version.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/Version.cpp b/code/Version.cpp index 2cd759817..fe9a2fdaa 100644 --- a/code/Version.cpp +++ b/code/Version.cpp @@ -46,7 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "ScenePrivate.h" static const unsigned int MajorVersion = 4; -static const unsigned int MinorVersion = 0; +static const unsigned int MinorVersion = 1; // -------------------------------------------------------------------------------- // Legal information string - dont't remove this. From 406a06705e005742b4ed36f92e3f09d9a222cfc8 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 11 Dec 2017 13:21:35 +0100 Subject: [PATCH 419/490] Update Doxyfile.in Update version --- doc/Doxyfile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 8d51259a8..ffe39f9f7 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -32,7 +32,7 @@ PROJECT_NAME = Assimp # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = "v3.1.1 (June 2014)" +PROJECT_NUMBER = "v4.1. (December 2018)" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer From 65d29c542091c21e72b143818b708333747e36ad Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 11 Dec 2017 13:22:20 +0100 Subject: [PATCH 420/490] Update CMakeLists.txt Update version --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0fc9af467..dd2317cca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -129,8 +129,8 @@ ENDIF(NOT BUILD_SHARED_LIBS) # Define here the needed parameters SET (ASSIMP_VERSION_MAJOR 4) -SET (ASSIMP_VERSION_MINOR 0) -SET (ASSIMP_VERSION_PATCH 1) +SET (ASSIMP_VERSION_MINOR 1) +SET (ASSIMP_VERSION_PATCH 0) SET (ASSIMP_VERSION ${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}.${ASSIMP_VERSION_PATCH}) SET (ASSIMP_SOVERSION 4) SET (PROJECT_VERSION "${ASSIMP_VERSION}") From e8139ef5159737b62f759edf6ccd6fcbd810ea32 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 11 Dec 2017 13:30:16 +0100 Subject: [PATCH 421/490] Update utVersion.cpp Fix unittests. --- test/unit/utVersion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/utVersion.cpp b/test/unit/utVersion.cpp index 677a131c9..a424a27cf 100644 --- a/test/unit/utVersion.cpp +++ b/test/unit/utVersion.cpp @@ -54,7 +54,7 @@ TEST_F( utVersion, aiGetLegalStringTest ) { } TEST_F( utVersion, aiGetVersionMinorTest ) { - EXPECT_EQ( aiGetVersionMinor(), 0U ); + EXPECT_EQ( aiGetVersionMinor(), 1U ); } TEST_F( utVersion, aiGetVersionMajorTest ) { From 7337474b6ccf850919377e4e4072d34397f81175 Mon Sep 17 00:00:00 2001 From: Teybeo Date: Mon, 11 Dec 2017 16:26:00 +0100 Subject: [PATCH 422/490] Fix 3MF define typo --- code/Exporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 3fc31a722..0281e4e99 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -165,7 +165,7 @@ Exporter::ExportFormatEntry gExporters[] = Exporter::ExportFormatEntry( "x3d", "Extensible 3D", "x3d" , &ExportSceneX3D, 0 ), #endif -#ifndef ASSIMP_BUILD_NO3MF_EXPORTER +#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 ) #endif }; From 8cd1ede9baf2476a5cae280b211afa31c3b3c60d Mon Sep 17 00:00:00 2001 From: Teybeo Date: Mon, 11 Dec 2017 16:26:45 +0100 Subject: [PATCH 423/490] Fix 3MF define typo --- code/D3MFExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index 162d20764..229e3f261 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -324,5 +324,5 @@ void D3MFExporter::writeRelInfoToFile( const std::string &folder, const std::str } // Namespace D3MF } // Namespace Assimp -#endif // ASSIMP_BUILD_NO3MF_EXPORTER +#endif // ASSIMP_BUILD_NO_3MF_EXPORTER #endif // ASSIMP_BUILD_NO_EXPORT From 99031997bdb87074138505da977122bc361dd57b Mon Sep 17 00:00:00 2001 From: Teybeo Date: Mon, 11 Dec 2017 16:27:27 +0100 Subject: [PATCH 424/490] Fix 3MF define typo --- code/D3MFExporter.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/D3MFExporter.h b/code/D3MFExporter.h index 16efabf29..b6be73976 100644 --- a/code/D3MFExporter.h +++ b/code/D3MFExporter.h @@ -59,7 +59,7 @@ class IOStream; namespace D3MF { #ifndef ASSIMP_BUILD_NO_EXPORT -#ifndef ASSIMP_BUILD_NO3MF_EXPORTER +#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER struct OpcPackageRelationship; @@ -95,7 +95,7 @@ private: std::vector mRelations; }; -#endif // ASSIMP_BUILD_NO3MF_EXPORTER +#endif // ASSIMP_BUILD_NO_3MF_EXPORTER #endif // ASSIMP_BUILD_NO_EXPORT } // Namespace D3MF From f9b599355c3b537bcb48f734d0a98ed1c22566d2 Mon Sep 17 00:00:00 2001 From: ilovezfs Date: Tue, 12 Dec 2017 02:28:03 -0800 Subject: [PATCH 425/490] unzip: fix build with older zlib Fixes "unzip.c:150:11: error: unknown type name 'z_crc_t'" --- contrib/unzip/unzip.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contrib/unzip/unzip.c b/contrib/unzip/unzip.c index e8b62e763..fea6e8913 100644 --- a/contrib/unzip/unzip.c +++ b/contrib/unzip/unzip.c @@ -41,6 +41,10 @@ woven in by Terry Thorsen 1/2003. #include "zlib.h" #include "unzip.h" +#if ZLIB_VERNUM < 0x1270 +typedef unsigned long z_crc_t; +#endif + #ifdef STDC # include # include From 30ae14fae9ca2d2a2aaa185395c75e615518af87 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 12 Dec 2017 18:40:28 +0200 Subject: [PATCH 426/490] B3DImporter: Add unique_to_array helper function --- code/B3DImporter.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/code/B3DImporter.cpp b/code/B3DImporter.cpp index bc888fb66..46008bf31 100644 --- a/code/B3DImporter.cpp +++ b/code/B3DImporter.cpp @@ -267,6 +267,21 @@ T *B3DImporter::to_array( const vector &v ){ return p; } + +// ------------------------------------------------------------------------------------------------ +template +T **unique_to_array( vector > &v ){ + if( v.empty() ) { + return 0; + } + T **p = new T*[ v.size() ]; + for( size_t i = 0; i < v.size(); ++i ){ + p[i] = v[i].release(); + } + return p; +} + + // ------------------------------------------------------------------------------------------------ void B3DImporter::ReadTEXS(){ while( ChunkSize() ){ From acab4c327e53d9cdd468536861a5f5575ff7ae51 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 12 Dec 2017 18:42:14 +0200 Subject: [PATCH 427/490] B3DImporter: Store animations in unique_ptr --- code/B3DImporter.cpp | 10 ++++------ code/B3DImporter.h | 3 ++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/code/B3DImporter.cpp b/code/B3DImporter.cpp index 46008bf31..b3a01c79c 100644 --- a/code/B3DImporter.cpp +++ b/code/B3DImporter.cpp @@ -93,7 +93,6 @@ void DeleteAllBarePointers(std::vector& x) B3DImporter::~B3DImporter() { - DeleteAllBarePointers(_animations); } // ------------------------------------------------------------------------------------------------ @@ -515,11 +514,11 @@ void B3DImporter::ReadANIM(){ int frames=ReadInt(); float fps=ReadFloat(); - aiAnimation *anim=new aiAnimation; - _animations.push_back( anim ); + std::unique_ptr anim(new aiAnimation); anim->mDuration=frames; anim->mTicksPerSecond=fps; + _animations.emplace_back( std::move(anim) ); } // ------------------------------------------------------------------------------------------------ @@ -601,7 +600,6 @@ void B3DImporter::ReadBB3D( aiScene *scene ){ _nodeAnims.clear(); - DeleteAllBarePointers(_animations); _animations.clear(); string t=ReadChunk(); @@ -715,12 +713,12 @@ void B3DImporter::ReadBB3D( aiScene *scene ){ //animations if( _animations.size()==1 && _nodeAnims.size() ){ - aiAnimation *anim=_animations.back(); + aiAnimation *anim = _animations.back().get(); anim->mNumChannels=static_cast(_nodeAnims.size()); anim->mChannels=to_array( _nodeAnims ); scene->mNumAnimations=static_cast(_animations.size()); - scene->mAnimations=to_array( _animations ); + scene->mAnimations=unique_to_array( _animations ); } // convert to RH diff --git a/code/B3DImporter.h b/code/B3DImporter.h index 94644edd4..b283cfab6 100644 --- a/code/B3DImporter.h +++ b/code/B3DImporter.h @@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include "BaseImporter.h" +#include #include struct aiNodeAnim; @@ -124,7 +125,7 @@ private: std::vector _nodes; std::vector _meshes; std::vector _nodeAnims; - std::vector _animations; + std::vector > _animations; }; } From f1707e920d9b0534757326dd5786e44774eae6a3 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 12 Dec 2017 18:54:41 +0200 Subject: [PATCH 428/490] B3DImporter: Store meshes in unique_ptr --- code/B3DImporter.cpp | 9 +++++---- code/B3DImporter.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/code/B3DImporter.cpp b/code/B3DImporter.cpp index b3a01c79c..d764bd530 100644 --- a/code/B3DImporter.cpp +++ b/code/B3DImporter.cpp @@ -400,8 +400,7 @@ void B3DImporter::ReadTRIS( int v0 ){ Fail( "Bad material id" ); } - aiMesh *mesh=new aiMesh; - _meshes.push_back( mesh ); + std::unique_ptr mesh(new aiMesh); mesh->mMaterialIndex=matid; mesh->mNumFaces=0; @@ -429,6 +428,8 @@ void B3DImporter::ReadTRIS( int v0 ){ ++mesh->mNumFaces; ++face; } + + _meshes.emplace_back( std::move(mesh) ); } // ------------------------------------------------------------------------------------------------ @@ -635,7 +636,7 @@ void B3DImporter::ReadBB3D( aiScene *scene ){ aiNode *node=_nodes[i]; for( size_t j=0;jmNumMeshes;++j ){ - aiMesh *mesh=_meshes[node->mMeshes[j]]; + aiMesh *mesh = _meshes[node->mMeshes[j]].get(); int n_tris=mesh->mNumFaces; int n_verts=mesh->mNumVertices=n_tris * 3; @@ -708,7 +709,7 @@ void B3DImporter::ReadBB3D( aiScene *scene ){ //meshes scene->mNumMeshes= static_cast(_meshes.size()); - scene->mMeshes=to_array( _meshes ); + scene->mMeshes = unique_to_array( _meshes ); //animations if( _animations.size()==1 && _nodeAnims.size() ){ diff --git a/code/B3DImporter.h b/code/B3DImporter.h index b283cfab6..ef36c4618 100644 --- a/code/B3DImporter.h +++ b/code/B3DImporter.h @@ -123,7 +123,7 @@ private: std::vector _vertices; std::vector _nodes; - std::vector _meshes; + std::vector > _meshes; std::vector _nodeAnims; std::vector > _animations; }; From 08a35d4e1f44030276293d4a64863e802cee8464 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 12 Dec 2017 19:16:06 +0200 Subject: [PATCH 429/490] B3DImporter: Store materials in unique_ptr --- code/B3DImporter.cpp | 8 ++++---- code/B3DImporter.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/code/B3DImporter.cpp b/code/B3DImporter.cpp index d764bd530..195ee04a8 100644 --- a/code/B3DImporter.cpp +++ b/code/B3DImporter.cpp @@ -309,8 +309,7 @@ void B3DImporter::ReadBRUS(){ /*int blend=**/ReadInt(); int fx=ReadInt(); - aiMaterial *mat=new aiMaterial; - _materials.push_back( mat ); + std::unique_ptr mat(new aiMaterial); // Name aiString ainame( name ); @@ -347,6 +346,7 @@ void B3DImporter::ReadBRUS(){ mat->AddProperty( &texname,AI_MATKEY_TEXTURE_DIFFUSE(0) ); } } + _materials.emplace_back( std::move(mat) ); } } @@ -702,10 +702,10 @@ void B3DImporter::ReadBB3D( aiScene *scene ){ //material if( !_materials.size() ){ - _materials.push_back( new aiMaterial ); + _materials.emplace_back( std::unique_ptr(new aiMaterial) ); } scene->mNumMaterials= static_cast(_materials.size()); - scene->mMaterials=to_array( _materials ); + scene->mMaterials = unique_to_array( _materials ); //meshes scene->mNumMeshes= static_cast(_meshes.size()); diff --git a/code/B3DImporter.h b/code/B3DImporter.h index ef36c4618..f9645f594 100644 --- a/code/B3DImporter.h +++ b/code/B3DImporter.h @@ -117,7 +117,7 @@ private: std::vector _stack; std::vector _textures; - std::vector _materials; + std::vector > _materials; int _vflags,_tcsets,_tcsize; std::vector _vertices; From 824dfc314b80dc0e91505cb08a65a50e4e8d2ded Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 12 Dec 2017 19:39:41 +0200 Subject: [PATCH 430/490] B3DImporter: Store node animations in unique_ptr --- code/B3DImporter.cpp | 13 ++++++++----- code/B3DImporter.h | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/code/B3DImporter.cpp b/code/B3DImporter.cpp index 195ee04a8..dd7b0d95e 100644 --- a/code/B3DImporter.cpp +++ b/code/B3DImporter.cpp @@ -546,7 +546,7 @@ aiNode *B3DImporter::ReadNODE( aiNode *parent ){ node->mParent=parent; node->mTransformation=tform; - aiNodeAnim *nodeAnim=0; + std::unique_ptr nodeAnim; vector meshes; vector children; @@ -564,11 +564,10 @@ aiNode *B3DImporter::ReadNODE( aiNode *parent ){ ReadANIM(); }else if( t=="KEYS" ){ if( !nodeAnim ){ - nodeAnim=new aiNodeAnim; - _nodeAnims.push_back( nodeAnim ); + nodeAnim.reset(new aiNodeAnim); nodeAnim->mNodeName=node->mName; } - ReadKEYS( nodeAnim ); + ReadKEYS( nodeAnim.get() ); }else if( t=="NODE" ){ aiNode *child=ReadNODE( node ); children.push_back( child ); @@ -576,6 +575,10 @@ aiNode *B3DImporter::ReadNODE( aiNode *parent ){ ExitChunk(); } + if (nodeAnim) { + _nodeAnims.emplace_back( std::move(nodeAnim) ); + } + node->mNumMeshes= static_cast(meshes.size()); node->mMeshes=to_array( meshes ); @@ -716,7 +719,7 @@ void B3DImporter::ReadBB3D( aiScene *scene ){ aiAnimation *anim = _animations.back().get(); anim->mNumChannels=static_cast(_nodeAnims.size()); - anim->mChannels=to_array( _nodeAnims ); + anim->mChannels = unique_to_array( _nodeAnims ); scene->mNumAnimations=static_cast(_animations.size()); scene->mAnimations=unique_to_array( _animations ); diff --git a/code/B3DImporter.h b/code/B3DImporter.h index f9645f594..342b88a28 100644 --- a/code/B3DImporter.h +++ b/code/B3DImporter.h @@ -124,7 +124,7 @@ private: std::vector _nodes; std::vector > _meshes; - std::vector _nodeAnims; + std::vector > _nodeAnims; std::vector > _animations; }; From 89afe0780b99e7309cf6158ce3e23e1e76f242f3 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 12 Dec 2017 20:03:16 +0200 Subject: [PATCH 431/490] B3DImporter: Fix double free when reusing Importer --- code/B3DImporter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/B3DImporter.cpp b/code/B3DImporter.cpp index dd7b0d95e..aa87609d4 100644 --- a/code/B3DImporter.cpp +++ b/code/B3DImporter.cpp @@ -702,6 +702,7 @@ void B3DImporter::ReadBB3D( aiScene *scene ){ //nodes scene->mRootNode=_nodes[0]; + _nodes.clear(); // node ownership now belongs to scene //material if( !_materials.size() ){ From 89ff8fc05d5a5f4517222b92a9642e4ab63b528b Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Tue, 12 Dec 2017 20:34:53 +0100 Subject: [PATCH 432/490] Add unit test for issue 1623 --- test/unit/utPLYImportExport.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/unit/utPLYImportExport.cpp b/test/unit/utPLYImportExport.cpp index 82cc54bdb..dbb7f4292 100644 --- a/test/unit/utPLYImportExport.cpp +++ b/test/unit/utPLYImportExport.cpp @@ -85,6 +85,18 @@ TEST_F(utPLYImportExport, exportTest_Success ) { #endif // ASSIMP_BUILD_NO_EXPORT +//Test issue 1623, crash when loading two PLY files in a row +TEST_F(utPLYImportExport, importerMultipleTest) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", 0); + + EXPECT_NE(nullptr, scene); + + scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", 0); + + EXPECT_NE(nullptr, scene); +} + TEST_F( utPLYImportExport, vertexColorTest ) { Assimp::Importer importer; const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/PLY/float-color.ply", 0 ); From 50bcaf39fd60da3bc9cb5a2dffb7f2f26d1731e9 Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Tue, 12 Dec 2017 20:48:51 +0100 Subject: [PATCH 433/490] Fix issue #1623 : crash when loading multiple PLY files Pointer mGeneratedMesh was not reset to nullptr when transfering ownership to pScene->mMeshes --- code/PlyLoader.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/code/PlyLoader.cpp b/code/PlyLoader.cpp index c6e862bf1..7c4614474 100644 --- a/code/PlyLoader.cpp +++ b/code/PlyLoader.cpp @@ -91,9 +91,9 @@ namespace // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer PLYImporter::PLYImporter() - : mBuffer() - , pcDOM() - , mGeneratedMesh(NULL){ + : mBuffer(nullptr) + , pcDOM(nullptr) + , mGeneratedMesh(nullptr){ // empty } @@ -196,7 +196,10 @@ void PLYImporter::InternReadFile(const std::string& pFile, if (!PLY::DOM::ParseInstance(streamedBuffer, &sPlyDom, this)) { if (mGeneratedMesh != NULL) + { delete(mGeneratedMesh); + mGeneratedMesh = nullptr; + } streamedBuffer.close(); throw DeadlyImportError("Invalid .ply file: Unable to build DOM (#1)"); @@ -211,7 +214,10 @@ void PLYImporter::InternReadFile(const std::string& pFile, if (!PLY::DOM::ParseInstanceBinary(streamedBuffer, &sPlyDom, this, bIsBE)) { if (mGeneratedMesh != NULL) + { delete(mGeneratedMesh); + mGeneratedMesh = nullptr; + } streamedBuffer.close(); throw DeadlyImportError("Invalid .ply file: Unable to build DOM (#2)"); @@ -220,7 +226,10 @@ void PLYImporter::InternReadFile(const std::string& pFile, else { if (mGeneratedMesh != NULL) + { delete(mGeneratedMesh); + mGeneratedMesh = nullptr; + } streamedBuffer.close(); throw DeadlyImportError("Invalid .ply file: Unknown file format"); @@ -230,7 +239,10 @@ void PLYImporter::InternReadFile(const std::string& pFile, { AI_DEBUG_INVALIDATE_PTR(this->mBuffer); if (mGeneratedMesh != NULL) + { delete(mGeneratedMesh); + mGeneratedMesh = nullptr; + } streamedBuffer.close(); throw DeadlyImportError("Invalid .ply file: Missing format specification"); @@ -252,7 +264,10 @@ void PLYImporter::InternReadFile(const std::string& pFile, if (mGeneratedMesh->mNumVertices < 3) { if (mGeneratedMesh != NULL) + { delete(mGeneratedMesh); + mGeneratedMesh = nullptr; + } streamedBuffer.close(); throw DeadlyImportError("Invalid .ply file: Not enough " @@ -289,6 +304,7 @@ void PLYImporter::InternReadFile(const std::string& pFile, pScene->mNumMeshes = 1; pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; pScene->mMeshes[0] = mGeneratedMesh; + mGeneratedMesh = nullptr; // generate a simple node structure pScene->mRootNode = new aiNode(); From 31a4ccaebb338deac016748e3cdfef1e2e5361fe Mon Sep 17 00:00:00 2001 From: Alexis Breust Date: Thu, 14 Dec 2017 16:11:12 +0100 Subject: [PATCH 434/490] Added support for generating glb2 (binary glTF 2) --- code/Exporter.cpp | 5 ++- code/glTF2AssetWriter.h | 1 + code/glTF2AssetWriter.inl | 92 +++++++++++++++++++++++++++++++++++++++ code/glTF2Exporter.cpp | 14 +++++- 4 files changed, 110 insertions(+), 2 deletions(-) diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 0281e4e99..cb496490f 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -92,6 +92,7 @@ void ExportScene3DS(const char*, IOSystem*, const aiScene*, const ExportProperti void ExportSceneGLTF(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneGLB(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneGLTF2(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneGLB2(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperties*); @@ -151,6 +152,8 @@ Exporter::ExportFormatEntry gExporters[] = aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf2", &ExportSceneGLTF2, aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), + Exporter::ExportFormatEntry( "glb2", "GL Transmission Format v. 2", "glb2", &ExportSceneGLB2, + aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), #endif #ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER @@ -420,7 +423,7 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c pimpl->mError = std::string("Found no exporter to handle this file format: ") + pFormatId; ASSIMP_END_EXCEPTION_REGION(aiReturn); - + return AI_FAILURE; } diff --git a/code/glTF2AssetWriter.h b/code/glTF2AssetWriter.h index bce2b1bd1..b4e7ffc2e 100644 --- a/code/glTF2AssetWriter.h +++ b/code/glTF2AssetWriter.h @@ -81,6 +81,7 @@ public: AssetWriter(Asset& asset); void WriteFile(const char* path); + void WriteGLBFile(const char* path); }; } diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index 8b2769a37..aaef7bccb 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -561,6 +561,98 @@ namespace glTF2 { } } + inline void AssetWriter::WriteGLBFile(const char* path) + { + std::unique_ptr outfile(mAsset.OpenFile(path, "wb", true)); + + if (outfile == 0) { + throw DeadlyExportError("Could not open output file: " + std::string(path)); + } + + // Padding with zeros make a invalid JSON for the gltf online validator + uint8_t padding[] = { '\n', '\n', '\n' }; + + // Adapt JSON so that it is not pointing to an external file, + // as this is required by the GLB spec'. + mDoc["buffers"][0].RemoveMember("uri"); + + // + // JSON chunk + // + + StringBuffer docBuffer; + Writer writer(docBuffer); + mDoc.Accept(writer); + + uint32_t jsonChunkLength = (docBuffer.GetSize() + 3) & ~3; // Round up to next multiple of 4 + auto paddingLength = jsonChunkLength - docBuffer.GetSize(); + std::cout << paddingLength << std::endl; + + GLB_Chunk jsonChunk; + jsonChunk.chunkLength = jsonChunkLength; + jsonChunk.chunkType = ChunkType_JSON; + AI_SWAP4(jsonChunk.chunkLength); + + outfile->Seek(sizeof(GLB_Header), aiOrigin_SET); + if (outfile->Write(&jsonChunk, 1, sizeof(GLB_Chunk)) != sizeof(GLB_Chunk)) { + throw DeadlyExportError("Failed to write scene data header!"); + } + if (outfile->Write(docBuffer.GetString(), 1, docBuffer.GetSize()) != docBuffer.GetSize()) { + throw DeadlyExportError("Failed to write scene data!"); + } + if (paddingLength && outfile->Write(padding, 1, paddingLength) != paddingLength) { + throw DeadlyExportError("Failed to write scene data padding!"); + } + + // + // Binary chunk + // + + uint32_t binaryChunkLength = 0; + if (mAsset.buffers.Size() > 0) { + Ref b = mAsset.buffers.Get(0u); + if (b->byteLength > 0) { + binaryChunkLength = (b->byteLength + 3) & ~3; // Round up to next multiple of 4 + auto paddingLength = binaryChunkLength - b->byteLength; + + GLB_Chunk binaryChunk; + binaryChunk.chunkLength = binaryChunkLength; + binaryChunk.chunkType = ChunkType_BIN; + AI_SWAP4(binaryChunk.chunkLength); + + size_t bodyOffset = sizeof(GLB_Header) + sizeof(GLB_Chunk) + jsonChunk.chunkLength; + outfile->Seek(bodyOffset, aiOrigin_SET); + if (outfile->Write(&binaryChunk, 1, sizeof(GLB_Chunk)) != sizeof(GLB_Chunk)) { + throw DeadlyExportError("Failed to write body data header!"); + } + if (outfile->Write(b->GetPointer(), 1, b->byteLength) != b->byteLength) { + throw DeadlyExportError("Failed to write body data!"); + } + if (paddingLength && outfile->Write(padding, 1, paddingLength) != paddingLength) { + throw DeadlyExportError("Failed to write body data padding!"); + } + } + } + + // + // Header + // + + GLB_Header header; + memcpy(header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic)); + + header.version = 2; + AI_SWAP4(header.version); + + header.length = uint32_t(sizeof(GLB_Header) + 2 * sizeof(GLB_Chunk) + jsonChunkLength + binaryChunkLength); + AI_SWAP4(header.length); + + outfile->Seek(0, aiOrigin_SET); + if (outfile->Write(&header, 1, sizeof(GLB_Header)) != sizeof(GLB_Header)) { + throw DeadlyExportError("Failed to write the header!"); + } + } + inline void AssetWriter::WriteMetadata() { Value asset; diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index c1a803c1f..82b95195f 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -77,6 +77,14 @@ namespace Assimp { glTF2Exporter exporter(pFile, pIOSystem, pScene, pProperties, false); } + // ------------------------------------------------------------------------------------------------ + // Worker function for exporting a scene to GLB. Prototyped and registered in Exporter.cpp + void ExportSceneGLB2(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties) + { + // invoke the exporter + glTF2Exporter exporter(pFile, pIOSystem, pScene, pProperties, true); + } + } // end of namespace Assimp glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const aiScene* pScene, @@ -118,7 +126,11 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai AssetWriter writer(*mAsset); - writer.WriteFile(filename); + if (isBinary) { + writer.WriteGLBFile(filename); + } else { + writer.WriteFile(filename); + } } /* From d09df8cc07ea81fce3e43332b72d6fabfaa95a72 Mon Sep 17 00:00:00 2001 From: Alexis Breust Date: Thu, 14 Dec 2017 16:18:17 +0100 Subject: [PATCH 435/490] Fixed leftover log --- code/Exporter.cpp | 2 +- code/glTF2AssetWriter.inl | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/code/Exporter.cpp b/code/Exporter.cpp index cb496490f..eebecd859 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -152,7 +152,7 @@ Exporter::ExportFormatEntry gExporters[] = aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf2", &ExportSceneGLTF2, aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), - Exporter::ExportFormatEntry( "glb2", "GL Transmission Format v. 2", "glb2", &ExportSceneGLB2, + Exporter::ExportFormatEntry( "glb2", "GL Transmission Format v. 2 (binary)", "glb2", &ExportSceneGLB2, aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), #endif diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index aaef7bccb..9734dc7c8 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -586,7 +586,6 @@ namespace glTF2 { uint32_t jsonChunkLength = (docBuffer.GetSize() + 3) & ~3; // Round up to next multiple of 4 auto paddingLength = jsonChunkLength - docBuffer.GetSize(); - std::cout << paddingLength << std::endl; GLB_Chunk jsonChunk; jsonChunk.chunkLength = jsonChunkLength; From 722420c5dc65d4f684e311b7ee969936d5b1543c Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 14 Dec 2017 16:51:47 +0100 Subject: [PATCH 436/490] diable unaigned pointer access temprary. --- code/MDLLoader.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/MDLLoader.cpp b/code/MDLLoader.cpp index 2025d79b3..873da4c3e 100644 --- a/code/MDLLoader.cpp +++ b/code/MDLLoader.cpp @@ -416,7 +416,8 @@ void MDLImporter::InternReadFile_Quake1( ) { // get the first frame in the group BE_NCONST MDL::GroupFrame* pcFrames2 = (BE_NCONST MDL::GroupFrame*)pcFrames; - pcFirstFrame = (BE_NCONST MDL::SimpleFrame*)(&pcFrames2->time + pcFrames->type); + //pcFirstFrame = (BE_NCONST MDL::SimpleFrame*)(&pcFrames2->time + pcFrames->type); + ai_assert( false && "Unaligned pointer" ); } BE_NCONST MDL::Vertex* pcVertices = (BE_NCONST MDL::Vertex*) ((pcFirstFrame->name) + sizeof(pcFirstFrame->name)); VALIDATE_FILE_SIZE((const unsigned char*)(pcVertices + pcHeader->num_verts)); From eb0608f2e9c1fa130ba41469783e8c150f40c9f9 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 14 Dec 2017 17:00:16 +0100 Subject: [PATCH 437/490] diable unaigned pointer access temprary, second one. --- code/MDLLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/MDLLoader.cpp b/code/MDLLoader.cpp index 873da4c3e..3a9f2c344 100644 --- a/code/MDLLoader.cpp +++ b/code/MDLLoader.cpp @@ -415,7 +415,7 @@ void MDLImporter::InternReadFile_Quake1( ) else { // get the first frame in the group - BE_NCONST MDL::GroupFrame* pcFrames2 = (BE_NCONST MDL::GroupFrame*)pcFrames; + //BE_NCONST MDL::GroupFrame* pcFrames2 = (BE_NCONST MDL::GroupFrame*)pcFrames; //pcFirstFrame = (BE_NCONST MDL::SimpleFrame*)(&pcFrames2->time + pcFrames->type); ai_assert( false && "Unaligned pointer" ); } From 151d71bc697bb61bebc9cd90d890908cfe1ec275 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 14 Dec 2017 17:21:17 +0100 Subject: [PATCH 438/490] fix misalignment in vector2 [] operator. --- include/assimp/vector2.inl | 10 +++++++++- include/assimp/vector3.inl | 1 - 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/assimp/vector2.inl b/include/assimp/vector2.inl index 5ce13eece..ee6c4d2d4 100644 --- a/include/assimp/vector2.inl +++ b/include/assimp/vector2.inl @@ -114,7 +114,15 @@ const aiVector2t& aiVector2t::operator /= (TReal f) { // ------------------------------------------------------------------------------------------------ template TReal aiVector2t::operator[](unsigned int i) const { - return *(&x + i); + switch (i) { + case 0: + return x; + case 1: + return y; + default: + break; + } + return x; } // ------------------------------------------------------------------------------------------------ diff --git a/include/assimp/vector3.inl b/include/assimp/vector3.inl index a074bb23a..2b132c1a5 100644 --- a/include/assimp/vector3.inl +++ b/include/assimp/vector3.inl @@ -142,7 +142,6 @@ AI_FORCE_INLINE aiVector3t& aiVector3t::operator *= (const aiMatri // ------------------------------------------------------------------------------------------------ template AI_FORCE_INLINE TReal aiVector3t::operator[](unsigned int i) const { -// return *(&x + i); switch (i) { case 0: return x; From d29173aaa90a4dcb7735d259644e7b9be605d642 Mon Sep 17 00:00:00 2001 From: Alexis Breust Date: Thu, 14 Dec 2017 17:32:18 +0100 Subject: [PATCH 439/490] Following specification for padding --- code/glTF2AssetWriter.inl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index 9734dc7c8..6b1a50887 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -569,8 +569,8 @@ namespace glTF2 { throw DeadlyExportError("Could not open output file: " + std::string(path)); } - // Padding with zeros make a invalid JSON for the gltf online validator - uint8_t padding[] = { '\n', '\n', '\n' }; + // Padding with spaces as required by the spec + uint32_t padding = 0x20202020; // Adapt JSON so that it is not pointing to an external file, // as this is required by the GLB spec'. @@ -599,7 +599,7 @@ namespace glTF2 { if (outfile->Write(docBuffer.GetString(), 1, docBuffer.GetSize()) != docBuffer.GetSize()) { throw DeadlyExportError("Failed to write scene data!"); } - if (paddingLength && outfile->Write(padding, 1, paddingLength) != paddingLength) { + if (paddingLength && outfile->Write(&padding, 1, paddingLength) != paddingLength) { throw DeadlyExportError("Failed to write scene data padding!"); } @@ -627,7 +627,7 @@ namespace glTF2 { if (outfile->Write(b->GetPointer(), 1, b->byteLength) != b->byteLength) { throw DeadlyExportError("Failed to write body data!"); } - if (paddingLength && outfile->Write(padding, 1, paddingLength) != paddingLength) { + if (paddingLength && outfile->Write(&padding, 1, paddingLength) != paddingLength) { throw DeadlyExportError("Failed to write body data padding!"); } } From e0649b68220ff41c24be5a8faf3e03de3b4ebf5b Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 14 Dec 2017 17:42:59 +0100 Subject: [PATCH 440/490] fix misalignment in vector2 [] operator, the non-const one. --- include/assimp/vector2.inl | 58 ++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/include/assimp/vector2.inl b/include/assimp/vector2.inl index ee6c4d2d4..97db2820c 100644 --- a/include/assimp/vector2.inl +++ b/include/assimp/vector2.inl @@ -60,24 +60,28 @@ aiVector2t::operator aiVector2t () const { } // ------------------------------------------------------------------------------------------------ template +inline void aiVector2t::Set( TReal pX, TReal pY) { x = pX; y = pY; } // ------------------------------------------------------------------------------------------------ template +inline TReal aiVector2t::SquareLength() const { return x*x + y*y; } // ------------------------------------------------------------------------------------------------ template +inline TReal aiVector2t::Length() const { return std::sqrt( SquareLength()); } // ------------------------------------------------------------------------------------------------ template +inline aiVector2t& aiVector2t::Normalize() { *this /= Length(); return *this; @@ -85,6 +89,7 @@ aiVector2t& aiVector2t::Normalize() { // ------------------------------------------------------------------------------------------------ template +inline const aiVector2t& aiVector2t::operator += (const aiVector2t& o) { x += o.x; y += o.y; return *this; @@ -92,6 +97,7 @@ const aiVector2t& aiVector2t::operator += (const aiVector2t& o) { // ------------------------------------------------------------------------------------------------ template +inline const aiVector2t& aiVector2t::operator -= (const aiVector2t& o) { x -= o.x; y -= o.y; return *this; @@ -99,6 +105,7 @@ const aiVector2t& aiVector2t::operator -= (const aiVector2t& o) { // ------------------------------------------------------------------------------------------------ template +inline const aiVector2t& aiVector2t::operator *= (TReal f) { x *= f; y *= f; return *this; @@ -106,6 +113,7 @@ const aiVector2t& aiVector2t::operator *= (TReal f) { // ------------------------------------------------------------------------------------------------ template +inline const aiVector2t& aiVector2t::operator /= (TReal f) { x /= f; y /= f; return *this; @@ -113,6 +121,7 @@ const aiVector2t& aiVector2t::operator /= (TReal f) { // ------------------------------------------------------------------------------------------------ template +inline TReal aiVector2t::operator[](unsigned int i) const { switch (i) { case 0: @@ -127,24 +136,36 @@ TReal aiVector2t::operator[](unsigned int i) const { // ------------------------------------------------------------------------------------------------ template +inline TReal& aiVector2t::operator[](unsigned int i) { - return *(&x + i); + switch (i) { + case 0: + return x; + case 1: + return y; + default: + break; + } + return x; } // ------------------------------------------------------------------------------------------------ template +inline bool aiVector2t::operator== (const aiVector2t& other) const { return x == other.x && y == other.y; } // ------------------------------------------------------------------------------------------------ template +inline bool aiVector2t::operator!= (const aiVector2t& other) const { return x != other.x || y != other.y; } // --------------------------------------------------------------------------- template +inline bool aiVector2t::Equal(const aiVector2t& other, TReal epsilon) const { return std::abs(x - other.x) <= epsilon && @@ -153,6 +174,7 @@ bool aiVector2t::Equal(const aiVector2t& other, TReal epsilon) const { // ------------------------------------------------------------------------------------------------ template +inline aiVector2t& aiVector2t::operator= (TReal f) { x = y = f; return *this; @@ -160,6 +182,7 @@ aiVector2t& aiVector2t::operator= (TReal f) { // ------------------------------------------------------------------------------------------------ template +inline const aiVector2t aiVector2t::SymMul(const aiVector2t& o) { return aiVector2t(x*o.x,y*o.y); } @@ -168,65 +191,64 @@ const aiVector2t aiVector2t::SymMul(const aiVector2t& o) { // ------------------------------------------------------------------------------------------------ // symmetric addition template -inline aiVector2t operator + (const aiVector2t& v1, const aiVector2t& v2) -{ +inline +inline aiVector2t operator + (const aiVector2t& v1, const aiVector2t& v2) { return aiVector2t( v1.x + v2.x, v1.y + v2.y); } // ------------------------------------------------------------------------------------------------ // symmetric subtraction template -inline aiVector2t operator - (const aiVector2t& v1, const aiVector2t& v2) -{ +inline +aiVector2t operator - (const aiVector2t& v1, const aiVector2t& v2) { return aiVector2t( v1.x - v2.x, v1.y - v2.y); } // ------------------------------------------------------------------------------------------------ // scalar product template -inline TReal operator * (const aiVector2t& v1, const aiVector2t& v2) -{ +inline +TReal operator * (const aiVector2t& v1, const aiVector2t& v2) { return v1.x*v2.x + v1.y*v2.y; } // ------------------------------------------------------------------------------------------------ // scalar multiplication template -inline aiVector2t operator * ( TReal f, const aiVector2t& v) -{ +inline +aiVector2t operator * ( TReal f, const aiVector2t& v) { return aiVector2t( f*v.x, f*v.y); } // ------------------------------------------------------------------------------------------------ // and the other way around template -inline aiVector2t operator * ( const aiVector2t& v, TReal f) -{ +inline +aiVector2t operator * ( const aiVector2t& v, TReal f) { return aiVector2t( f*v.x, f*v.y); } // ------------------------------------------------------------------------------------------------ // scalar division template -inline aiVector2t operator / ( const aiVector2t& v, TReal f) -{ - +inline +aiVector2t operator / ( const aiVector2t& v, TReal f) { return v * (1/f); } // ------------------------------------------------------------------------------------------------ // vector division template -inline aiVector2t operator / ( const aiVector2t& v, const aiVector2t& v2) -{ +inline +aiVector2t operator / ( const aiVector2t& v, const aiVector2t& v2) { return aiVector2t(v.x / v2.x,v.y / v2.y); } // ------------------------------------------------------------------------------------------------ // vector negation template -inline aiVector2t operator - ( const aiVector2t& v) -{ +inline +aiVector2t operator - ( const aiVector2t& v) { return aiVector2t( -v.x, -v.y); } From 245b9b4bb1a1de421c0b95e92b187c04de8f7af8 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 14 Dec 2017 17:52:47 +0100 Subject: [PATCH 441/490] fix typo. --- include/assimp/vector2.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/assimp/vector2.inl b/include/assimp/vector2.inl index 97db2820c..1682a27ec 100644 --- a/include/assimp/vector2.inl +++ b/include/assimp/vector2.inl @@ -192,7 +192,7 @@ const aiVector2t aiVector2t::SymMul(const aiVector2t& o) { // symmetric addition template inline -inline aiVector2t operator + (const aiVector2t& v1, const aiVector2t& v2) { +aiVector2t operator + (const aiVector2t& v1, const aiVector2t& v2) { return aiVector2t( v1.x + v2.x, v1.y + v2.y); } From bef219a2d9cf40b8927c8bd24ac41ddd69c5aa2b Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 14 Dec 2017 17:59:23 +0100 Subject: [PATCH 442/490] fix access violation in vector2. --- include/assimp/vector2.h | 1 - include/assimp/vector2.inl | 15 --------------- 2 files changed, 16 deletions(-) diff --git a/include/assimp/vector2.h b/include/assimp/vector2.h index 564d1f8b5..df3c41024 100644 --- a/include/assimp/vector2.h +++ b/include/assimp/vector2.h @@ -85,7 +85,6 @@ public: const aiVector2t& operator /= (TReal f); TReal operator[](unsigned int i) const; - TReal& operator[](unsigned int i); bool operator== (const aiVector2t& other) const; bool operator!= (const aiVector2t& other) const; diff --git a/include/assimp/vector2.inl b/include/assimp/vector2.inl index 1682a27ec..d6171955f 100644 --- a/include/assimp/vector2.inl +++ b/include/assimp/vector2.inl @@ -134,21 +134,6 @@ TReal aiVector2t::operator[](unsigned int i) const { return x; } -// ------------------------------------------------------------------------------------------------ -template -inline -TReal& aiVector2t::operator[](unsigned int i) { - switch (i) { - case 0: - return x; - case 1: - return y; - default: - break; - } - return x; -} - // ------------------------------------------------------------------------------------------------ template inline From dec3e2ba42e60be8c4ae3ba2a2f08d32ead77d51 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 14 Dec 2017 18:06:28 +0100 Subject: [PATCH 443/490] XGLLoader: fix const issue when seeting vec2. --- code/XGLLoader.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/code/XGLLoader.cpp b/code/XGLLoader.cpp index 8ef91afac..97fe4fbd5 100644 --- a/code/XGLLoader.cpp +++ b/code/XGLLoader.cpp @@ -904,12 +904,14 @@ aiVector2D XGLImporter::ReadVec2() } const char* s = m_reader->getNodeData(); - for(int i = 0; i < 2; ++i) { + ai_real v[2]; + for(int i = 0; i < 2; ++i) { if(!SkipSpaces(&s)) { LogError("unexpected EOL, failed to parse vec2"); return vec; } - vec[i] = fast_atof(&s); + + v[i] = fast_atof(&s); SkipSpaces(&s); if (i != 1 && *s != ',') { @@ -918,6 +920,8 @@ aiVector2D XGLImporter::ReadVec2() } ++s; } + vec.x = v[0]; + vex.y = v[1]; return vec; } From 5e63ba9a9d762f311e2fd1b9222ceca033abbcee Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 14 Dec 2017 18:10:42 +0100 Subject: [PATCH 444/490] typo. --- code/XGLLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/XGLLoader.cpp b/code/XGLLoader.cpp index 97fe4fbd5..809936099 100644 --- a/code/XGLLoader.cpp +++ b/code/XGLLoader.cpp @@ -921,7 +921,7 @@ aiVector2D XGLImporter::ReadVec2() ++s; } vec.x = v[0]; - vex.y = v[1]; + vec.y = v[1]; return vec; } From 32e5e3a5764cbce871a11c4d9ecf58c75bac328e Mon Sep 17 00:00:00 2001 From: Alexis Breust Date: Fri, 15 Dec 2017 15:40:31 +0100 Subject: [PATCH 445/490] Forgot to uncomment isBinary --- code/glTF2Exporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index 82b95195f..22ceb57d2 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -88,7 +88,7 @@ namespace Assimp { } // end of namespace Assimp glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const aiScene* pScene, - const ExportProperties* pProperties, bool /*isBinary*/) + const ExportProperties* pProperties, bool isBinary) : mFilename(filename) , mIOSystem(pIOSystem) , mProperties(pProperties) From 379562055b8061480e44a56354bb778c8e5449a7 Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 15 Dec 2017 23:18:45 +0100 Subject: [PATCH 446/490] Fix incorrect NO_GLTF_IMPORTER define name in glTFExporter.h --- code/glTFExporter.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/glTFExporter.h b/code/glTFExporter.h index c813fff44..752072604 100644 --- a/code/glTFExporter.h +++ b/code/glTFExporter.h @@ -45,7 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef AI_GLTFEXPORTER_H_INC #define AI_GLTFEXPORTER_H_INC -#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER +#ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER #include #include @@ -113,6 +113,6 @@ namespace Assimp } -#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER +#endif // ASSIMP_BUILD_NO_GLTF_EXPORTER #endif // AI_GLTFEXPORTER_H_INC From 068d4aa4cbcd88f6f4313374a0e562424e534467 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 13 Dec 2017 10:55:32 +0200 Subject: [PATCH 447/490] Disable clang static analysis for now Travis updated to clang 5.0 and there are new issues. --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9265dfb38..0b8d2f328 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,9 +33,10 @@ env: matrix: include: - - os: linux - compiler: clang - env: ANALYZE=ON + # disabled until clang 5.0 analyzer issues are fixed + # - os: linux + # compiler: clang + # env: ANALYZE=ON - os: linux compiler: clang env: ASAN=ON From 276fc5f46609b9a81b0a4ad0a3a6020f1708b746 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 13 Dec 2017 11:27:05 +0200 Subject: [PATCH 448/490] Remove packed attribute from aiUVTransform It would cause a warning when removing packed from aiVector2 --- include/assimp/material.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/assimp/material.h b/include/assimp/material.h index a12e7d076..502b89746 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -491,7 +491,7 @@ struct aiUVTransform } #endif -} PACK_STRUCT; +}; #include "./Compiler/poppack1.h" From 45ad63f37353fc2e84bccfb9daf32fff6fb3cbf8 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 13 Dec 2017 11:27:44 +0200 Subject: [PATCH 449/490] Remove packed attribute from aiVector2 Returning a reference to a member of packed member is UB --- include/assimp/vector2.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/assimp/vector2.h b/include/assimp/vector2.h index 564d1f8b5..3b85fc22b 100644 --- a/include/assimp/vector2.h +++ b/include/assimp/vector2.h @@ -99,7 +99,7 @@ public: operator aiVector2t () const; TReal x, y; -} PACK_STRUCT; +}; typedef aiVector2t aiVector2D; From 87462165b5243a96ddf4e4d1725e16e4aac3af5c Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Wed, 13 Dec 2017 11:28:22 +0200 Subject: [PATCH 450/490] Fix bad pointer arithmetic in aiVector2 Trying to reference 'y' via pointer arithmetic on 'x' is UB --- include/assimp/vector2.inl | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/include/assimp/vector2.inl b/include/assimp/vector2.inl index 5ce13eece..fe3059ad5 100644 --- a/include/assimp/vector2.inl +++ b/include/assimp/vector2.inl @@ -114,13 +114,29 @@ const aiVector2t& aiVector2t::operator /= (TReal f) { // ------------------------------------------------------------------------------------------------ template TReal aiVector2t::operator[](unsigned int i) const { - return *(&x + i); + switch (i) { + case 0: + return x; + case 1: + return y; + default: + break; + } + return x; } // ------------------------------------------------------------------------------------------------ template TReal& aiVector2t::operator[](unsigned int i) { - return *(&x + i); + switch (i) { + case 0: + return x; + case 1: + return y; + default: + break; + } + return x; } // ------------------------------------------------------------------------------------------------ From 2f082340fc668cd8fef56232d64fa824ce2b325d Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 16 Dec 2017 14:57:59 +0200 Subject: [PATCH 451/490] MDLLoader: Add workaround for clang 5.0 build issue --- code/MDLLoader.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/MDLLoader.cpp b/code/MDLLoader.cpp index 2025d79b3..dc7b379ce 100644 --- a/code/MDLLoader.cpp +++ b/code/MDLLoader.cpp @@ -415,8 +415,14 @@ void MDLImporter::InternReadFile_Quake1( ) else { // get the first frame in the group +#if 1 + // FIXME: the cast is wrong and causea a warning on clang 5.0 + // disable thi code for now, fix it later + ai_assert(false && "Bad pointer cast"); +#else BE_NCONST MDL::GroupFrame* pcFrames2 = (BE_NCONST MDL::GroupFrame*)pcFrames; pcFirstFrame = (BE_NCONST MDL::SimpleFrame*)(&pcFrames2->time + pcFrames->type); +#endif } BE_NCONST MDL::Vertex* pcVertices = (BE_NCONST MDL::Vertex*) ((pcFirstFrame->name) + sizeof(pcFirstFrame->name)); VALIDATE_FILE_SIZE((const unsigned char*)(pcVertices + pcHeader->num_verts)); From 463dec5c7ed89088614c9f84acc61a9d5f987368 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 16 Dec 2017 15:19:53 +0200 Subject: [PATCH 452/490] Change StreamReader::IncPtr argument to signed Negative values are passed to it so it needs to be signed --- code/StreamReader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/StreamReader.h b/code/StreamReader.h index 6220de9a8..b70ee7eca 100644 --- a/code/StreamReader.h +++ b/code/StreamReader.h @@ -192,7 +192,7 @@ public: // --------------------------------------------------------------------- /** Increase the file pointer (relative seeking) */ - void IncPtr(size_t plus) { + void IncPtr(intptr_t plus) { current += plus; if (current > limit) { throw DeadlyImportError("End of file or read limit was reached"); From 47c422ea49b1bbbd51b7715d371926b7d305757c Mon Sep 17 00:00:00 2001 From: Tommy Date: Sun, 17 Dec 2017 16:40:09 +0100 Subject: [PATCH 453/490] Fix non-ascii encoding in comments in FBXMaterial.cpp. It looks like it was just saved with a wrong encoding, as these 0xB4 characters were in place of "'". Also converted tabs to spaces to match indent in the rest of the file. --- code/FBXMaterial.cpp | 52 ++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/code/FBXMaterial.cpp b/code/FBXMaterial.cpp index a80f243ba..130993d28 100644 --- a/code/FBXMaterial.cpp +++ b/code/FBXMaterial.cpp @@ -291,40 +291,40 @@ Video::Video(uint64_t id, const Element& element, const Document& doc, const std if(FileName) { fileName = ParseTokenAsString(GetRequiredToken(*FileName,0)); - } + } if(RelativeFilename) { relativeFileName = ParseTokenAsString(GetRequiredToken(*RelativeFilename,0)); } if(Content) { - //this field is ommited when the embedded texture is already loaded, let's ignore if it´s not found - try { - const Token& token = GetRequiredToken(*Content, 0); - const char* data = token.begin(); - if (!token.IsBinary()) { - DOMWarning("video content is not binary data, ignoring", &element); - } - else if (static_cast(token.end() - data) < 5) { - DOMError("binary data array is too short, need five (5) bytes for type signature and element count", &element); - } - else if (*data != 'R') { - DOMWarning("video content is not raw binary data, ignoring", &element); - } - else { - // read number of elements - uint32_t len = 0; - ::memcpy(&len, data + 1, sizeof(len)); - AI_SWAP4(len); + //this field is ommited when the embedded texture is already loaded, let's ignore if it's not found + try { + const Token& token = GetRequiredToken(*Content, 0); + const char* data = token.begin(); + if (!token.IsBinary()) { + DOMWarning("video content is not binary data, ignoring", &element); + } + else if (static_cast(token.end() - data) < 5) { + DOMError("binary data array is too short, need five (5) bytes for type signature and element count", &element); + } + else if (*data != 'R') { + DOMWarning("video content is not raw binary data, ignoring", &element); + } + else { + // read number of elements + uint32_t len = 0; + ::memcpy(&len, data + 1, sizeof(len)); + AI_SWAP4(len); - contentLength = len; + contentLength = len; - content = new uint8_t[len]; - ::memcpy(content, data + 5, len); - } - } catch (runtime_error runtimeError) { - //we don´t need the content data for contents that has already been loaded - } + content = new uint8_t[len]; + ::memcpy(content, data + 5, len); + } + } catch (runtime_error runtimeError) { + //we don't need the content data for contents that has already been loaded + } } props = GetPropertyTable(doc,"Video.FbxVideo",element,sc); From 4623c2f14c121e4792e74169b6cef09631a4184a Mon Sep 17 00:00:00 2001 From: rickomax Date: Mon, 18 Dec 2017 18:14:54 -0200 Subject: [PATCH 454/490] FBX Embedding Fix FBX files may use a texture reference from an embedded texture that hasn't been loaded yet. This patch fixes this issue, storing all texture filenames, that can be acessed later via "scene::GetEmbeddedTexture", when all textures are already loaded. Some warnings have been added to other file formats that uses embedded data. --- code/ColladaLoader.cpp | 5 +++++ code/FBXConverter.cpp | 30 ++++++++++++------------------ code/FBXImportSettings.h | 8 ++++---- code/FBXImporter.cpp | 2 +- include/assimp/scene.h | 21 +++++++++++++++++++++ include/assimp/texture.h | 6 ++++++ 6 files changed, 49 insertions(+), 23 deletions(-) diff --git a/code/ColladaLoader.cpp b/code/ColladaLoader.cpp index d2141c374..d41f4a999 100644 --- a/code/ColladaLoader.cpp +++ b/code/ColladaLoader.cpp @@ -1778,6 +1778,11 @@ aiString ColladaLoader::FindFilenameForEffectTexture( const ColladaParser& pPars tex->pcData = (aiTexel*)new char[tex->mWidth]; memcpy(tex->pcData,&imIt->second.mImageData[0],tex->mWidth); + // TODO: check the possibility of using the flag "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING" + // In FBX files textures are now stored internally by Assimp with their filename included + // Now Assimp can lookup thru the loaded textures after all data is processed + // We need to load all textures before referencing them, as FBX file format order may reference a texture before loading it + // This may occur on this case too, it has to be studied // setup texture reference string result.data[0] = '*'; result.length = 1 + ASSIMP_itoa10(result.data+1,static_cast(MAXLEN-1),static_cast(mTextures.size())); diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index 24bdfdd11..66f541f17 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -436,18 +436,6 @@ private: aiScene* const out; const FBX::Document& doc; - - bool FindTextureIndexByFilename(const Video& video, unsigned int& index) { - index = 0; - const char* videoFileName = video.FileName().c_str(); - for (auto texture = textures_converted.begin(); texture != textures_converted.end(); ++texture) { - if (!strcmp(texture->first->FileName().c_str(), videoFileName)) { - index = texture->second; - return true; - } - } - return false; - } }; Converter::Converter( aiScene* out, const Document& doc ) @@ -1776,6 +1764,8 @@ unsigned int Converter::ConvertVideo( const Video& video ) memcpy( out_tex->achFormatHint, ext.c_str(), ext.size() ); } + out_tex->mFilename.Set(video.FileName().c_str()); + return static_cast( textures.size() - 1 ); } @@ -1810,15 +1800,19 @@ void Converter::TrySetTextureProperties( aiMaterial* out_mat, const TextureMap& textures_converted[media] = index; textureReady = true; } - else if (doc.Settings().searchEmbeddedTextures) { //try to find the texture on the already-loaded textures by the filename, if the flag is on - textureReady = FindTextureIndexByFilename(*media, index); - } } // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture), if the texture is ready - if (textureReady) { - path.data[0] = '*'; - path.length = 1 + ASSIMP_itoa10(path.data + 1, MAXLEN - 1, index); + if (doc.Settings().useLegacyEmbeddedTextureNaming) { + if (textureReady) { + // TODO: check the possibility of using the flag "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING" + // In FBX files textures are now stored internally by Assimp with their filename included + // Now Assimp can lookup thru the loaded textures after all data is processed + // We need to load all textures before referencing them, as FBX file format order may reference a texture before loading it + // This may occur on this case too, it has to be studied + path.data[0] = '*'; + path.length = 1 + ASSIMP_itoa10(path.data + 1, MAXLEN - 1, index); + } } } diff --git a/code/FBXImportSettings.h b/code/FBXImportSettings.h index 53fa64ec6..84129cd4a 100644 --- a/code/FBXImportSettings.h +++ b/code/FBXImportSettings.h @@ -63,7 +63,7 @@ struct ImportSettings , readWeights(true) , preservePivots(true) , optimizeEmptyAnimationCurves(true) - , searchEmbeddedTextures(false) + , useLegacyEmbeddedTextureNaming(false) {} @@ -139,9 +139,9 @@ struct ImportSettings * The default value is true. */ bool optimizeEmptyAnimationCurves; - /** search for embedded loaded textures, where no embedded texture data is provided. - * The default value is false. */ - bool searchEmbeddedTextures; + /** use legacy naming for embedded textures eg: (*0, *1, *2) + **/ + bool useLegacyEmbeddedTextureNaming; }; diff --git a/code/FBXImporter.cpp b/code/FBXImporter.cpp index 51e41b8f4..37d7cc4be 100644 --- a/code/FBXImporter.cpp +++ b/code/FBXImporter.cpp @@ -135,7 +135,7 @@ void FBXImporter::SetupProperties(const Importer* pImp) settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false); settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true); settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true); - settings.searchEmbeddedTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_SEARCH_EMBEDDED_TEXTURES, false); + settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false); } // ------------------------------------------------------------------------------------------------ diff --git a/include/assimp/scene.h b/include/assimp/scene.h index 342c316d6..5d6fbab13 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -366,6 +366,27 @@ struct aiScene return mAnimations != NULL && mNumAnimations > 0; } + //! Returns a short filename from a full path + static const char* GetShortFilename(const char* filename) { + const char* lastSlash = strrchr(filename, '/'); + if (lastSlash == '\0') { + lastSlash = strrchr(filename, '\\'); + } + const char* shortFilename = lastSlash != '\0' ? lastSlash + 1 : filename; + return shortFilename; + } + + //! Returns an embedded texture + const aiTexture* GetEmbeddedTexture(const char* filename) { + const char* shortFilename = GetShortFilename(filename); + for (unsigned int i = 0; i < mNumTextures; i++) { + const char* shortTextureFilename = GetShortFilename(mTextures[i]->mFilename.C_Str()); + if (strcmp(shortTextureFilename, shortFilename) == 0) { + return mTextures[i]; + } + } + return nullptr; + } #endif // __cplusplus /** Internal data, do not touch */ diff --git a/include/assimp/texture.h b/include/assimp/texture.h index c09ef2cbe..ec65e4655 100644 --- a/include/assimp/texture.h +++ b/include/assimp/texture.h @@ -179,6 +179,12 @@ struct aiTexture */ C_STRUCT aiTexel* pcData; + /** Texture original filename + * + * Used to get the texture reference + */ + C_STRUCT aiString mFilename; + #ifdef __cplusplus //! For compressed textures (mHeight == 0): compare the From 987d3150895b017191f7671c56975d71de8b87ea Mon Sep 17 00:00:00 2001 From: rickomax Date: Mon, 18 Dec 2017 18:53:25 -0200 Subject: [PATCH 455/490] Fixed config file Fixed config file --- include/assimp/config.h.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/assimp/config.h.in b/include/assimp/config.h.in index 1dabae7cf..ad08a04b6 100644 --- a/include/assimp/config.h.in +++ b/include/assimp/config.h.in @@ -642,13 +642,13 @@ enum aiComponent "IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES" // --------------------------------------------------------------------------- -/** @brief Set whether the fbx importer will search for embedded loaded textures, where no embedded texture data is provided. +/** @brief Set whether the fbx importer will use the legacy embedded texture naming. * * The default value is false (0) * Property type: bool */ -#define AI_CONFIG_IMPORT_FBX_SEARCH_EMBEDDED_TEXTURES \ - "IMPORT_FBX_SEARCH_EMBEDDED_TEXTURES" +#define AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING \ + "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING" // --------------------------------------------------------------------------- /** @brief Set the vertex animation keyframe to be imported From 9c9e7a2e821d351a4099dad29cc15257e84eece6 Mon Sep 17 00:00:00 2001 From: rickomax Date: Mon, 18 Dec 2017 19:18:11 -0200 Subject: [PATCH 456/490] Fixed char comparision Fixed char comparision --- include/assimp/scene.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/assimp/scene.h b/include/assimp/scene.h index 5d6fbab13..00fa42142 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -369,10 +369,10 @@ struct aiScene //! Returns a short filename from a full path static const char* GetShortFilename(const char* filename) { const char* lastSlash = strrchr(filename, '/'); - if (lastSlash == '\0') { + if (lastSlash == nullptr) { lastSlash = strrchr(filename, '\\'); } - const char* shortFilename = lastSlash != '\0' ? lastSlash + 1 : filename; + const char* shortFilename = lastSlash != nullptr ? lastSlash + 1 : filename; return shortFilename; } From b6800a9992db079edcdfd5891fdd843de3a2328c Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 19 Dec 2017 18:16:41 +0200 Subject: [PATCH 457/490] X3DImporter: Add virtual destructors to some classes which already have virtual functions --- code/FIReader.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/FIReader.hpp b/code/FIReader.hpp index 5f4e5bb48..e142a571b 100644 --- a/code/FIReader.hpp +++ b/code/FIReader.hpp @@ -62,6 +62,7 @@ namespace Assimp { struct FIValue { virtual const std::string &toString() const = 0; + virtual ~FIValue() {} }; struct FIStringValue: public FIValue { @@ -121,6 +122,7 @@ struct FICDATAValue: public FIStringValue { struct FIDecoder { virtual std::shared_ptr decode(const uint8_t *data, size_t len) = 0; + virtual ~FIDecoder() {} }; struct FIQName { From 1e9f329e6daca02c0c8163bea6b3130c90b7bfc5 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 19 Dec 2017 18:18:14 +0200 Subject: [PATCH 458/490] MMD: Add virtual destructor to a class which already has virtual functions --- code/MMDPmxParser.h | 1 + 1 file changed, 1 insertion(+) diff --git a/code/MMDPmxParser.h b/code/MMDPmxParser.h index 989cffed6..a26eddb04 100644 --- a/code/MMDPmxParser.h +++ b/code/MMDPmxParser.h @@ -87,6 +87,7 @@ namespace pmx { public: virtual void Read(std::istream *stream, PmxSetting *setting) = 0; + virtual ~PmxVertexSkinning() {} }; class PmxVertexSkinningBDEF1 : public PmxVertexSkinning From 65ffeaa81e10efcd49b1fe0d29ffe8ca51bbf8da Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 19 Dec 2017 18:24:03 +0200 Subject: [PATCH 459/490] ObjImporter: Use unique_ptr --- code/ObjFileImporter.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/ObjFileImporter.cpp b/code/ObjFileImporter.cpp index 9f3bdef97..5fa57e108 100644 --- a/code/ObjFileImporter.cpp +++ b/code/ObjFileImporter.cpp @@ -317,7 +317,7 @@ aiMesh *ObjFileImporter::createTopology( const ObjFile::Model* pModel, const Obj return NULL; } - aiMesh* pMesh = new aiMesh; + std::unique_ptr pMesh(new aiMesh); if( !pObjMesh->m_name.empty() ) { pMesh->mName.Set( pObjMesh->m_name ); } @@ -382,9 +382,9 @@ aiMesh *ObjFileImporter::createTopology( const ObjFile::Model* pModel, const Obj } // Create mesh vertices - createVertexArray(pModel, pData, meshIndex, pMesh, uiIdxCount); + createVertexArray(pModel, pData, meshIndex, pMesh.get(), uiIdxCount); - return pMesh; + return pMesh.release(); } // ------------------------------------------------------------------------------------------------ From 0cf772a4d44de87f0db1c026ec40892c181475a1 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 19 Dec 2017 18:46:48 +0200 Subject: [PATCH 460/490] MDCLoader: Replace min and strlen with strnlen --- code/MDCLoader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/MDCLoader.cpp b/code/MDCLoader.cpp index 21aca53ff..8b3140108 100644 --- a/code/MDCLoader.cpp +++ b/code/MDCLoader.cpp @@ -294,8 +294,8 @@ void MDCImporter::InternReadFile( pcMesh->mMaterialIndex = (unsigned int)aszShaders.size(); // create a new shader - aszShaders.push_back(std::string( pcShader->ucName, std::min( - ::strlen(pcShader->ucName),sizeof(pcShader->ucName)) )); + aszShaders.push_back(std::string( pcShader->ucName, + ::strnlen(pcShader->ucName, sizeof(pcShader->ucName)) )); } // need to create a default material else if (UINT_MAX == iDefaultMatIndex) From 47b725a8c8c9f783a959256bfa25eea10b516dd4 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 19 Dec 2017 18:55:14 +0200 Subject: [PATCH 461/490] MDCLoader: Fix horrible pointer casting hack --- code/MDCLoader.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/code/MDCLoader.cpp b/code/MDCLoader.cpp index 8b3140108..8af18992d 100644 --- a/code/MDCLoader.cpp +++ b/code/MDCLoader.cpp @@ -283,9 +283,8 @@ void MDCImporter::InternReadFile( pcMesh->mNumVertices = pcMesh->mNumFaces * 3; // store the name of the surface for use as node name. - // FIX: make sure there is a 0 termination - const_cast(pcSurface->ucName[AI_MDC_MAXQPATH-1]) = '\0'; - pcMesh->mTextureCoords[3] = (aiVector3D*)pcSurface->ucName; + pcMesh->mName.Set(std::string(pcSurface->ucName + , strnlen(pcSurface->ucName, AI_MDC_MAXQPATH - 1))); // go to the first shader in the file. ignore the others. if (pcSurface->ulNumShaders) @@ -432,7 +431,7 @@ void MDCImporter::InternReadFile( else if (1 == pScene->mNumMeshes) { pScene->mRootNode = new aiNode(); - pScene->mRootNode->mName.Set(std::string((const char*)pScene->mMeshes[0]->mTextureCoords[3])); + pScene->mRootNode->mName = pScene->mMeshes[0]->mName; pScene->mRootNode->mNumMeshes = 1; pScene->mRootNode->mMeshes = new unsigned int[1]; pScene->mRootNode->mMeshes[0] = 0; @@ -447,17 +446,13 @@ void MDCImporter::InternReadFile( { aiNode* pcNode = pScene->mRootNode->mChildren[i] = new aiNode(); pcNode->mParent = pScene->mRootNode; - pcNode->mName.Set(std::string((const char*)pScene->mMeshes[i]->mTextureCoords[3])); + pcNode->mName = pScene->mMeshes[i]->mName; pcNode->mNumMeshes = 1; pcNode->mMeshes = new unsigned int[1]; pcNode->mMeshes[0] = i; } } - // make sure we invalidate the pointer to the mesh name - for (unsigned int i = 0; i < pScene->mNumMeshes;++i) - pScene->mMeshes[i]->mTextureCoords[3] = NULL; - // create materials pScene->mNumMaterials = (unsigned int)aszShaders.size(); pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; From c44522d4db95f685db0cedd2f0b85102f152b623 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 19 Dec 2017 19:38:38 +0200 Subject: [PATCH 462/490] ObjImporter: Fix possible memory leak --- code/ObjFileImporter.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/code/ObjFileImporter.cpp b/code/ObjFileImporter.cpp index 5fa57e108..e1a084d62 100644 --- a/code/ObjFileImporter.cpp +++ b/code/ObjFileImporter.cpp @@ -264,8 +264,12 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile { unsigned int meshId = pObject->m_Meshes[ i ]; aiMesh *pMesh = createTopology( pModel, pObject, meshId ); - if( pMesh && pMesh->mNumFaces > 0 ) { - MeshArray.push_back( pMesh ); + if( pMesh ) { + if (pMesh->mNumFaces > 0) { + MeshArray.push_back( pMesh ); + } else { + delete pMesh; + } } } From bb5495f99a7835bc2bdcfe2dc68fb086f67defec Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 19 Dec 2017 19:39:04 +0200 Subject: [PATCH 463/490] Q3BSP: Add assertion to silence a static analyzer warning --- code/Q3BSPFileImporter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/Q3BSPFileImporter.cpp b/code/Q3BSPFileImporter.cpp index 9d6d8e870..0cdafae42 100644 --- a/code/Q3BSPFileImporter.cpp +++ b/code/Q3BSPFileImporter.cpp @@ -447,6 +447,7 @@ void Q3BSPFileImporter::createTriangleTopology( const Q3BSP::Q3BSPModel *pModel, pMesh->mTextureCoords[ 0 ][ rVertIdx ].Set( pVertex->vTexCoord.x, pVertex->vTexCoord.y, 0.0f ); pMesh->mTextureCoords[ 1 ][ rVertIdx ].Set( pVertex->vLightmap.x, pVertex->vLightmap.y, 0.0f ); + ai_assert( m_pCurrentFace ); m_pCurrentFace->mIndices[ idx ] = rVertIdx; rVertIdx++; From 06179cee04b5247831a0e3bd72db23ce6815d739 Mon Sep 17 00:00:00 2001 From: Giuseppe Barbieri Date: Tue, 19 Dec 2017 22:53:59 +0100 Subject: [PATCH 464/490] Update FBXDocument.cpp --- code/FBXDocument.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/FBXDocument.cpp b/code/FBXDocument.cpp index 9592bf31f..54f18b191 100644 --- a/code/FBXDocument.cpp +++ b/code/FBXDocument.cpp @@ -619,10 +619,10 @@ std::vector Document::GetConnectionsBySourceSequenced(uint64_ } // ------------------------------------------------------------------------------------------------ -std::vector Document::GetConnectionsBySourceSequenced(uint64_t dest, const char* classname) const +std::vector Document::GetConnectionsBySourceSequenced(uint64_t src, const char* classname) const { const char* arr[] = {classname}; - return GetConnectionsBySourceSequenced(dest, arr,1); + return GetConnectionsBySourceSequenced(src, arr,1); } // ------------------------------------------------------------------------------------------------ From 5a30bccdae08c856adf09e3a41e31c9e5fd3a939 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 22 Dec 2017 16:45:07 +0100 Subject: [PATCH 465/490] closes https://github.com/assimp/assimp/issues/1612: make wstaring handling depend from encoding of the filename. --- code/DefaultIOSystem.cpp | 64 ++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/code/DefaultIOSystem.cpp b/code/DefaultIOSystem.cpp index fe4a219c0..d83d2a79f 100644 --- a/code/DefaultIOSystem.cpp +++ b/code/DefaultIOSystem.cpp @@ -74,10 +74,22 @@ bool DefaultIOSystem::Exists( const char* pFile) const { #ifdef _WIN32 wchar_t fileName16[PATHLIMIT]; - MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, pFile, -1, fileName16, PATHLIMIT); - struct _stat64 filestat; - if (0 != _wstat64(fileName16, &filestat)) - return false; + + bool isUnicode = IsTextUnicode(pFile, strlen(pFile), NULL); + if (isUnicode) { + + MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, pFile, -1, fileName16, PATHLIMIT); + struct _stat64 filestat; + if (0 != _wstat64(fileName16, &filestat)) { + return false; + } + } else { + FILE* file = ::fopen(pFile, "rb"); + if (!file) + return false; + + ::fclose(file); + } #else FILE* file = ::fopen( pFile, "rb"); if( !file) @@ -97,9 +109,14 @@ IOStream* DefaultIOSystem::Open( const char* strFile, const char* strMode) FILE* file; #ifdef _WIN32 wchar_t fileName16[PATHLIMIT]; - MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, strFile, -1, fileName16, PATHLIMIT); - std::string mode8(strMode); - file = ::_wfopen(fileName16, std::wstring(mode8.begin(), mode8.end()).c_str()); + bool isUnicode = IsTextUnicode(strFile, strlen(strFile), NULL ); + if (isUnicode) { + MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, strFile, -1, fileName16, PATHLIMIT); + std::string mode8(strMode); + file = ::_wfopen(fileName16, std::wstring(mode8.begin(), mode8.end()).c_str()); + } else { + file = ::fopen(strFile, strMode); + } #else file = ::fopen(strFile, strMode); #endif @@ -140,24 +157,41 @@ inline static void MakeAbsolutePath (const char* in, char* _out) { ai_assert(in && _out); #if defined( _MSC_VER ) || defined( __MINGW32__ ) - wchar_t out16[PATHLIMIT]; - wchar_t in16[PATHLIMIT]; - MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, in, -1, out16, PATHLIMIT); - wchar_t* ret = ::_wfullpath( out16, in16, PATHLIMIT ); - if (ret) - { - WideCharToMultiByte(CP_UTF8, MB_PRECOMPOSED, out16, -1, _out, PATHLIMIT, nullptr, nullptr); + bool isUnicode = IsTextUnicode(in, strlen(in), NULL); + if (isUnicode) { + wchar_t out16[PATHLIMIT]; + wchar_t in16[PATHLIMIT]; + MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, in, -1, out16, PATHLIMIT); + wchar_t* ret = ::_wfullpath(out16, in16, PATHLIMIT); + if (ret) { + WideCharToMultiByte(CP_UTF8, MB_PRECOMPOSED, out16, -1, _out, PATHLIMIT, nullptr, nullptr); + } + if (!ret) { + // preserve the input path, maybe someone else is able to fix + // the path before it is accessed (e.g. our file system filter) + DefaultLogger::get()->warn("Invalid path: " + std::string(in)); + strcpy(_out, in); + } + + } else { + char* ret = :: _fullpath(_out, in, PATHLIMIT); + if (!ret) { + // preserve the input path, maybe someone else is able to fix + // the path before it is accessed (e.g. our file system filter) + DefaultLogger::get()->warn("Invalid path: " + std::string(in)); + strcpy(_out, in); + } } #else // use realpath char* ret = realpath(in, _out); -#endif if(!ret) { // preserve the input path, maybe someone else is able to fix // the path before it is accessed (e.g. our file system filter) DefaultLogger::get()->warn("Invalid path: "+std::string(in)); strcpy(_out,in); } +#endif } // ------------------------------------------------------------------------------------------------ From 4a65e76ca760cc71eda68ceb35a7d05b7c0b58e6 Mon Sep 17 00:00:00 2001 From: Faule Socke Date: Sat, 23 Dec 2017 17:34:39 +0100 Subject: [PATCH 466/490] Fix buffer overflow in obj loader The overflow-checking code in ObjFileImporter::createVertexArray is at the wrong position, allowing buffer overflows in preceding code. This fix moves the code to the right spot. An actual overflow can be caused by usign some more bugs and weird behaviours and injecting a malformed line statement into the object file, containing only one index. Such a malformed file could for example look like: o 1 v 0 0 0 v 1 1 1 v 2 2 2 l 1 f 1 2 3 Because the code in ObjFileImporter::createTopology incorrectly handles line-type faces containing only one index (in line 364), it underestimates the number of required indices and therefore causes the buffer allocated in line 421 to be too small. I believe, the correct fix for this would be in the parser and rejecting such faces early. However the overflow check was misplaced anyway. If you can't reproduce a crash, just insert some more "l 1" lines before the "f 1 2 3" line until it crashes. The behaviour of heap buffer overflows strongly depends on memory layout and allocation history. --- code/ObjFileImporter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/ObjFileImporter.cpp b/code/ObjFileImporter.cpp index e1a084d62..10c463c36 100644 --- a/code/ObjFileImporter.cpp +++ b/code/ObjFileImporter.cpp @@ -448,6 +448,10 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel, throw DeadlyImportError( "OBJ: vertex index out of range" ); } + if ( pMesh->mNumVertices <= newIndex ) { + throw DeadlyImportError("OBJ: bad vertex index"); + } + pMesh->mVertices[ newIndex ] = pModel->m_Vertices[ vertex ]; // Copy all normals @@ -479,10 +483,6 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel, pMesh->mTextureCoords[ 0 ][ newIndex ] = aiVector3D( coord3d.x, coord3d.y, coord3d.z ); } - if ( pMesh->mNumVertices <= newIndex ) { - throw DeadlyImportError("OBJ: bad vertex index"); - } - // Get destination face aiFace *pDestFace = &pMesh->mFaces[ outIndex ]; From 1ad789bae9d5f38d17ae525037fbbc4ce450f1b6 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sun, 24 Dec 2017 14:26:53 +0200 Subject: [PATCH 467/490] Raw: Reformat code, no functional change --- code/RawLoader.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/RawLoader.cpp b/code/RawLoader.cpp index 2b76455d7..f8ca4e689 100644 --- a/code/RawLoader.cpp +++ b/code/RawLoader.cpp @@ -243,8 +243,10 @@ void RAWImporter::InternReadFile( const std::string& pFile, { cc = &pScene->mRootNode; pScene->mRootNode->mNumChildren = 0; + } else { + cc = new aiNode*[pScene->mRootNode->mNumChildren]; + pScene->mRootNode->mChildren = cc; } - else cc = pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren]; pScene->mNumMaterials = pScene->mNumMeshes; aiMaterial** mats = pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; From 7932a85ca16d8e87b008a1634d6f45a28383246c Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sun, 24 Dec 2017 14:38:26 +0200 Subject: [PATCH 468/490] Raw: Fix unitialized values in scene --- code/RawLoader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/RawLoader.cpp b/code/RawLoader.cpp index f8ca4e689..13deb0005 100644 --- a/code/RawLoader.cpp +++ b/code/RawLoader.cpp @@ -245,6 +245,7 @@ void RAWImporter::InternReadFile( const std::string& pFile, pScene->mRootNode->mNumChildren = 0; } else { cc = new aiNode*[pScene->mRootNode->mNumChildren]; + memset(cc, 0, sizeof(aiNode*) * pScene->mRootNode->mNumChildren); pScene->mRootNode->mChildren = cc; } From 407854382727d793937ce0f4155d49cece1c7e1f Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sun, 24 Dec 2017 14:57:14 +0200 Subject: [PATCH 469/490] OpenGEX: Throw exception on malformed color4 instead of crashing --- code/OpenGEXImporter.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 025356c1d..5448edba5 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -778,10 +778,22 @@ static void fillColor4( aiColor4D *col4, Value *vals ) { Value *next( vals ); col4->r = next->getFloat(); next = next->m_next; + if (!next) { + throw DeadlyImportError( "OpenGEX: Not enough values to fill 4-element color, only 1" ); + } + col4->g = next->getFloat(); next = next->m_next; + if (!next) { + throw DeadlyImportError( "OpenGEX: Not enough values to fill 4-element color, only 2" ); + } + col4->b = next->getFloat(); next = next->m_next; + if (!next) { + throw DeadlyImportError( "OpenGEX: Not enough values to fill 4-element color, only 3" ); + } + col4->a = next->getFloat(); } From 0cc25491a4324d9e7dc7fb085c825a79d6c3c6d6 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sun, 24 Dec 2017 15:25:42 +0200 Subject: [PATCH 470/490] irrXML: Remove horrible hack --- contrib/irrXML/CXMLReaderImpl.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/contrib/irrXML/CXMLReaderImpl.h b/contrib/irrXML/CXMLReaderImpl.h index 63b700dc1..7d33b9404 100644 --- a/contrib/irrXML/CXMLReaderImpl.h +++ b/contrib/irrXML/CXMLReaderImpl.h @@ -9,6 +9,8 @@ #include "irrString.h" #include "irrArray.h" +#include + using namespace Assimp; #ifdef _DEBUG @@ -664,12 +666,9 @@ private: TextData = new char_type[sizeWithoutHeader]; // MSVC debugger complains here about loss of data ... - - - // FIXME - gcc complains about 'shift width larger than width of type' - // for T == unsigned long. Avoid it by messing around volatile .. - volatile unsigned int c = 3; - const src_char_type cc = (src_char_type)((((uint64_t)1u << (sizeof( char_type)< Date: Sun, 24 Dec 2017 22:14:39 +0200 Subject: [PATCH 471/490] RemoveComments: Fix out-of-bounds read when file ends with a comment --- code/RemoveComments.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/RemoveComments.cpp b/code/RemoveComments.cpp index 37d74124d..1ed94ea4d 100644 --- a/code/RemoveComments.cpp +++ b/code/RemoveComments.cpp @@ -66,6 +66,10 @@ void CommentRemover::RemoveLineComments(const char* szComment, if (!strncmp(szBuffer,szComment,len)) { while (!IsLineEnd(*szBuffer)) *szBuffer++ = chReplacement; + + if (!*szBuffer) { + break; + } } ++szBuffer; } From 55e69272bd73435630c1f8c7c230b2eaa3bf19f8 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sun, 24 Dec 2017 22:15:19 +0200 Subject: [PATCH 472/490] MMD: Remove bogus assert It can be triggered by input file, it's undocumented and it looks like nothing breaks --- code/MMDImporter.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/MMDImporter.cpp b/code/MMDImporter.cpp index 01f009519..c813063ab 100644 --- a/code/MMDImporter.cpp +++ b/code/MMDImporter.cpp @@ -141,8 +141,6 @@ void MMDImporter::CreateDataFromImport(const pmx::PmxModel *pModel, aiNode *pNode = new aiNode; if (!pModel->model_name.empty()) { pNode->mName.Set(pModel->model_name); - } else { - ai_assert(false); } pScene->mRootNode = pNode; From dc94e5921e2ee2db974fc65a0c14d36d2cdb3675 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sun, 24 Dec 2017 22:17:11 +0200 Subject: [PATCH 473/490] MDLImporter: Use unique_ptr Fixes a double free --- code/MDLMaterialLoader.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/code/MDLMaterialLoader.cpp b/code/MDLMaterialLoader.cpp index 9086925aa..4d23d0aa6 100644 --- a/code/MDLMaterialLoader.cpp +++ b/code/MDLMaterialLoader.cpp @@ -56,6 +56,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include "qnan.h" +#include + using namespace Assimp; static aiTexel* const bad_texel = reinterpret_cast(SIZE_MAX); @@ -489,7 +491,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7( unsigned int iWidth, unsigned int iHeight) { - aiTexture* pcNew = nullptr; + std::unique_ptr pcNew; // get the type of the skin unsigned int iMasked = (unsigned int)(iType & 0xF); @@ -509,7 +511,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7( "but texture height is not equal to 1, which is not supported by MED"); } - pcNew = new aiTexture(); + pcNew.reset(new aiTexture()); pcNew->mHeight = 0; pcNew->mWidth = iWidth; @@ -546,7 +548,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7( } else if (iMasked || !iType || (iType && iWidth && iHeight)) { - pcNew = new aiTexture(); + pcNew.reset(new aiTexture()); if (!iHeight || !iWidth) { DefaultLogger::get()->warn("Found embedded texture, but its width " @@ -577,7 +579,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7( pcNew->mHeight = iHeight; unsigned int iSkip = 0; - ParseTextureColorData(szCurrent,iMasked,&iSkip,pcNew); + ParseTextureColorData(szCurrent,iMasked,&iSkip,pcNew.get()); // skip length of texture data szCurrent += iSkip; @@ -588,7 +590,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7( // texture instead of material colors ... posssible they have // been converted to MDL7 from other formats, such as MDL5 aiColor4D clrTexture; - if (pcNew)clrTexture = ReplaceTextureWithColor(pcNew); + if (pcNew)clrTexture = ReplaceTextureWithColor(pcNew.get()); else clrTexture.r = get_qnan(); // check whether a material definition is contained in the skin @@ -680,8 +682,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7( // we don't need the texture anymore if (is_not_qnan(clrTexture.r)) { - delete pcNew; - pcNew = NULL; + pcNew.reset(); } // If an ASCII effect description (HLSL?) is contained in the file, @@ -716,7 +717,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7( { pScene->mNumTextures = 1; pScene->mTextures = new aiTexture*[1]; - pScene->mTextures[0] = pcNew; + pScene->mTextures[0] = pcNew.release(); } else { @@ -726,16 +727,13 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7( pScene->mTextures[i] = pc[i]; } - pScene->mTextures[pScene->mNumTextures] = pcNew; + pScene->mTextures[pScene->mNumTextures] = pcNew.release(); pScene->mNumTextures++; delete[] pc; } } VALIDATE_FILE_SIZE(szCurrent); *szCurrentOut = szCurrent; - if ( nullptr != pcNew ) { - delete pcNew; - } } // ------------------------------------------------------------------------------------------------ From 096056b899d9c423cdcad527849126e3e3e17a34 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sun, 24 Dec 2017 22:17:45 +0200 Subject: [PATCH 474/490] Q3BSP: Fix build with clang libc++ --- code/Q3BSPZipArchive.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/Q3BSPZipArchive.cpp b/code/Q3BSPZipArchive.cpp index 86c966d8e..1c8b18ad3 100644 --- a/code/Q3BSPZipArchive.cpp +++ b/code/Q3BSPZipArchive.cpp @@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "Q3BSPZipArchive.h" #include +#include #include namespace Assimp { From e5891e5a098e8637fb4ef414a1c8986f9f26bc99 Mon Sep 17 00:00:00 2001 From: Jean-Louis Boudrand Date: Wed, 27 Dec 2017 19:08:20 +0100 Subject: [PATCH 475/490] Qt viewer : Improve exports - Add a dialog to select the exporter - Export based on exporter id instead of extensions (fix issues like for Collada) - Message box if there is an error --- tools/assimp_qt_viewer/mainwindow.cpp | 36 ++++++++++++++++----------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/tools/assimp_qt_viewer/mainwindow.cpp b/tools/assimp_qt_viewer/mainwindow.cpp index 82ddc7092..65021c284 100644 --- a/tools/assimp_qt_viewer/mainwindow.cpp +++ b/tools/assimp_qt_viewer/mainwindow.cpp @@ -265,6 +265,8 @@ QString filename, filter, format_id; Exporter exporter; QTime time_begin; aiReturn rv; +QStringList exporterList; +QMap exportersMap; if(mScene == nullptr) { @@ -273,34 +275,40 @@ aiReturn rv; return; } - // build filter + for (int i = 0; i < exporter.GetExportFormatCount(); ++i) { - aiString filter_temp; - - mImporter.GetExtensionList(filter_temp); - filter = filter_temp.C_Str(); - filter.replace(';', ' '); + const aiExportFormatDesc* desc = exporter.GetExportFormatDescription(i); + exporterList.push_back(desc->id + QString(": ") + desc->description); + exportersMap.insert(desc->id, desc); } + // get an exporter + bool dialogSelectExporterOk; + QString selectedExporter = QInputDialog::getItem(this, "Export format", "Select the exporter : ", exporterList, 0, false, &dialogSelectExporterOk); + if (!dialogSelectExporterOk) + return; + + // build the filter + QString selectedId = selectedExporter.left(selectedExporter.indexOf(':')); + filter = QString("*.") + exportersMap[selectedId]->fileExtension; + // get file path filename = QFileDialog::getSaveFileName(this, "Set file name", "", filter); - // extract format ID - format_id = filename.right(filename.length() - filename.lastIndexOf('.') - 1); - if(format_id.isEmpty()) - { - QMessageBox::critical(this, "Export error", "File name must has extension."); - + // if it's canceled + if (filename == "") return; - } // begin export time_begin = QTime::currentTime(); - rv = exporter.Export(mScene, format_id.toLocal8Bit(), filename.toLocal8Bit(), aiProcess_FlipUVs); + rv = exporter.Export(mScene, selectedId.toLocal8Bit(), filename.toLocal8Bit(), aiProcess_FlipUVs); ui->lblExportTime->setText(QString("%1").arg(time_begin.secsTo(QTime::currentTime()))); if(rv == aiReturn_SUCCESS) LogInfo("Export done: " + filename); else + { LogError("Export failed: " + filename); + QMessageBox::critical(this, "Error", "Export failed: " + filename); + } } void MainWindow::on_cbxLighting_clicked(bool pChecked) From e030d374b94ffa7256eaf31573b2faa5fc2e94f8 Mon Sep 17 00:00:00 2001 From: Jean-Louis Boudrand Date: Thu, 28 Dec 2017 13:55:41 +0100 Subject: [PATCH 476/490] Qt Viewer : Fixes for failed imports Fix the UI when an import fail --- tools/assimp_qt_viewer/mainwindow.cpp | 37 +++++++++++++++++++-------- tools/assimp_qt_viewer/mainwindow.hpp | 5 ++++ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/tools/assimp_qt_viewer/mainwindow.cpp b/tools/assimp_qt_viewer/mainwindow.cpp index 65021c284..0dffec2c6 100644 --- a/tools/assimp_qt_viewer/mainwindow.cpp +++ b/tools/assimp_qt_viewer/mainwindow.cpp @@ -45,16 +45,16 @@ QTime time_begin = QTime::currentTime(); ui->leFileName->setText(pFileName.right(pFileName.length() - pFileName.lastIndexOf('/') - 1)); ui->lstLight->clear(); ui->lstCamera->clear(); - ui->cbxLighting->setChecked(true), mGLView->Lighting_Enable(); - ui->cbxBBox->setChecked(false); mGLView->Enable_SceneBBox(false); - ui->cbxTextures->setChecked(true), mGLView->Enable_Textures(true); + ui->cbxLighting->setChecked(true); mGLView->Lighting_Enable(); + ui->cbxBBox->setChecked(false); mGLView->Enable_SceneBBox(false); + ui->cbxTextures->setChecked(true); mGLView->Enable_Textures(true); // // Fill info labels // // Cameras - ui->lblCameraCount->setText(QString("%1").arg(mScene->mNumCameras)); + ui->lblCameraCount->setText(QString::number(mScene->mNumCameras)); // Lights - ui->lblLightCount->setText(QString("%1").arg(mScene->mNumLights)); + ui->lblLightCount->setText(QString::number(mScene->mNumLights)); // Meshes, faces, vertices. size_t qty_face = 0; size_t qty_vert = 0; @@ -65,9 +65,9 @@ QTime time_begin = QTime::currentTime(); qty_vert += mScene->mMeshes[idx_mesh]->mNumVertices; } - ui->lblMeshCount->setText(QString("%1").arg(mScene->mNumMeshes)); - ui->lblFaceCount->setText(QString("%1").arg(qty_face)); - ui->lblVertexCount->setText(QString("%1").arg(qty_vert)); + ui->lblMeshCount->setText(QString::number(mScene->mNumMeshes)); + ui->lblFaceCount->setText(QString::number(qty_face)); + ui->lblVertexCount->setText(QString::number(qty_vert)); // Animation if(mScene->mNumAnimations) ui->lblHasAnimation->setText("yes"); @@ -87,11 +87,26 @@ QTime time_begin = QTime::currentTime(); } else { - ui->lblLoadTime->clear(); - LogError(QString("Error parsing \'%1\' : \'%2\'").arg(pFileName).arg(mImporter.GetErrorString())); + ResetSceneInfos(); + + QString errorMessage = QString("Error parsing \'%1\' : \'%2\'").arg(pFileName).arg(mImporter.GetErrorString(); + QMessageBox::critical(this, "Import error", errorMessage); + LogError(errorMessage); }// if(mScene != nullptr) } +void MainWindow::ResetSceneInfos() +{ + ui->lblLoadTime->clear(); + ui->leFileName->clear(); + ui->lblMeshCount->setText("0"); + ui->lblFaceCount->setText("0"); + ui->lblVertexCount->setText("0"); + ui->lblCameraCount->setText("0"); + ui->lblLightCount->setText("0"); + ui->lblHasAnimation->setText("no"); +} + /********************************************************************/ /************************ Logging functions *************************/ /********************************************************************/ @@ -202,6 +217,8 @@ using namespace Assimp; mLoggerView = new CLoggerView(ui->tbLog); DefaultLogger::create("", Logger::VERBOSE); DefaultLogger::get()->attachStream(mLoggerView, DefaultLogger::Debugging | DefaultLogger::Info | DefaultLogger::Err | DefaultLogger::Warn); + + ResetSceneInfos(); } MainWindow::~MainWindow() diff --git a/tools/assimp_qt_viewer/mainwindow.hpp b/tools/assimp_qt_viewer/mainwindow.hpp index b20acb884..350b37abf 100644 --- a/tools/assimp_qt_viewer/mainwindow.hpp +++ b/tools/assimp_qt_viewer/mainwindow.hpp @@ -52,6 +52,11 @@ private: /// \param [in] pFileName - path and name of the file. void ImportFile(const QString& pFileName); + + /// \fn void ResetSceneInfos() + /// Reset informations about the scene + void ResetSceneInfos(); + /********************************************************************/ /************************ Logging functions *************************/ /********************************************************************/ From 6a9df80f688ffd7d6af0562e71299aadc2b50d5f Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 28 Dec 2017 14:52:41 +0100 Subject: [PATCH 477/490] closes https://github.com/assimp/assimp/issues/1655: add typedef for android NDK. --- contrib/zlib/zlib.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contrib/zlib/zlib.h b/contrib/zlib/zlib.h index 577d81e3b..2d6bb2976 100644 --- a/contrib/zlib/zlib.h +++ b/contrib/zlib/zlib.h @@ -77,6 +77,9 @@ extern "C" { the consistency of the compressed data, so the library should never crash even in the case of corrupted input. */ +#ifdef __ANDROID__ +using zcrc_t = unsigned_long; +#endif typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); typedef void (*free_func) OF((voidpf opaque, voidpf address)); From 04d5f49f6d24b7067899fc2bf44b887fbe1b1651 Mon Sep 17 00:00:00 2001 From: Jean-Louis Boudrand Date: Thu, 28 Dec 2017 15:10:55 +0100 Subject: [PATCH 478/490] Use QString::number easier to read + typos --- tools/assimp_qt_viewer/mainwindow.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tools/assimp_qt_viewer/mainwindow.cpp b/tools/assimp_qt_viewer/mainwindow.cpp index 0dffec2c6..acfc8e589 100644 --- a/tools/assimp_qt_viewer/mainwindow.cpp +++ b/tools/assimp_qt_viewer/mainwindow.cpp @@ -39,7 +39,7 @@ QTime time_begin = QTime::currentTime(); aiProcess_GenUVCoords | aiProcess_TransformUVCoords | aiProcess_FlipUVs); if(mScene != nullptr) { - ui->lblLoadTime->setText(QString("%1").arg(time_begin.secsTo(QTime::currentTime()))); + ui->lblLoadTime->setText(QString::number(time_begin.secsTo(QTime::currentTime()))); LogInfo("Import done: " + pFileName); // Prepare widgets for new scene. ui->leFileName->setText(pFileName.right(pFileName.length() - pFileName.lastIndexOf('/') - 1)); @@ -89,7 +89,7 @@ QTime time_begin = QTime::currentTime(); { ResetSceneInfos(); - QString errorMessage = QString("Error parsing \'%1\' : \'%2\'").arg(pFileName).arg(mImporter.GetErrorString(); + QString errorMessage = QString("Error parsing \'%1\' : \'%2\'").arg(pFileName).arg(mImporter.GetErrorString()); QMessageBox::critical(this, "Import error", errorMessage); LogError(errorMessage); }// if(mScene != nullptr) @@ -240,8 +240,8 @@ using namespace Assimp; void MainWindow::Paint_Finished(const size_t pPaintTime_ms, const GLfloat pDistance) { - ui->lblRenderTime->setText(QString("%1").arg(pPaintTime_ms)); - ui->lblDistance->setText(QString("%1").arg(pDistance)); + ui->lblRenderTime->setText(QString::number(pPaintTime_ms)); + ui->lblDistance->setText(QString::number(pDistance)); } void MainWindow::SceneObject_Camera(const QString& pName) @@ -282,9 +282,10 @@ QString filename, filter, format_id; Exporter exporter; QTime time_begin; aiReturn rv; -QStringList exporterList; +QStringList exportersList; QMap exportersMap; + if(mScene == nullptr) { QMessageBox::critical(this, "Export error", "Scene is empty"); @@ -295,13 +296,13 @@ QMap exportersMap; for (int i = 0; i < exporter.GetExportFormatCount(); ++i) { const aiExportFormatDesc* desc = exporter.GetExportFormatDescription(i); - exporterList.push_back(desc->id + QString(": ") + desc->description); + exportersList.push_back(desc->id + QString(": ") + desc->description); exportersMap.insert(desc->id, desc); } // get an exporter bool dialogSelectExporterOk; - QString selectedExporter = QInputDialog::getItem(this, "Export format", "Select the exporter : ", exporterList, 0, false, &dialogSelectExporterOk); + QString selectedExporter = QInputDialog::getItem(this, "Export format", "Select the exporter : ", exportersList, 0, false, &dialogSelectExporterOk); if (!dialogSelectExporterOk) return; @@ -318,13 +319,14 @@ QMap exportersMap; // begin export time_begin = QTime::currentTime(); rv = exporter.Export(mScene, selectedId.toLocal8Bit(), filename.toLocal8Bit(), aiProcess_FlipUVs); - ui->lblExportTime->setText(QString("%1").arg(time_begin.secsTo(QTime::currentTime()))); + ui->lblExportTime->setText(QString::number(time_begin.secsTo(QTime::currentTime()))); if(rv == aiReturn_SUCCESS) LogInfo("Export done: " + filename); else { - LogError("Export failed: " + filename); - QMessageBox::critical(this, "Error", "Export failed: " + filename); + QString errorMessage = QString("Export failed: ") + filename; + LogError(errorMessage); + QMessageBox::critical(this, "Export error", errorMessage); } } From 20ef807982a8ebe970dcd135d7d0631010421bf1 Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Fri, 29 Dec 2017 17:56:30 +0100 Subject: [PATCH 479/490] Remove assertion in ObjFileImporter This assertion is already handled by an exception, and could lead to a crash when parsing an ill-formed OBJ file. --- code/ObjFileImporter.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/code/ObjFileImporter.cpp b/code/ObjFileImporter.cpp index 10c463c36..11f4b9487 100644 --- a/code/ObjFileImporter.cpp +++ b/code/ObjFileImporter.cpp @@ -474,7 +474,6 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel, if ( !pModel->m_TextureCoord.empty() && vertexIndex < pSourceFace->m_texturCoords.size()) { const unsigned int tex = pSourceFace->m_texturCoords.at( vertexIndex ); - ai_assert( tex < pModel->m_TextureCoord.size() ); if ( tex >= pModel->m_TextureCoord.size() ) throw DeadlyImportError("OBJ: texture coordinate index out of range"); From 7de7c548fb5e49a2881a3899570e137c19796ee3 Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Fri, 29 Dec 2017 18:06:20 +0100 Subject: [PATCH 480/490] Add unit test to OBj importer for homogeneous coord This test triggers an assertion when we divide by zero in homogeneous coordinates. --- test/unit/utObjImportExport.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/unit/utObjImportExport.cpp b/test/unit/utObjImportExport.cpp index dd8adcd80..a6c91fed6 100644 --- a/test/unit/utObjImportExport.cpp +++ b/test/unit/utObjImportExport.cpp @@ -330,6 +330,18 @@ TEST_F(utObjImportExport, homogeneous_coordinates_Test) { EXPECT_EQ(vertice.z, 0.8f); } +TEST_F(utObjImportExport, homogeneous_coordinates_divide_by_zero_Test) { + static const std::string ObjModel = + "v -0.500000 0.000000 0.400000 0.\n" + "v -0.500000 0.000000 -0.800000 1.00000\n" + "v 0.500000 1.000000 -0.800000 0.5000\n" + "f 1 2 3\nB"; + + Assimp::Importer myimporter; + const aiScene *scene = myimporter.ReadFileFromMemory(ObjModel.c_str(), ObjModel.size(), aiProcess_ValidateDataStructure); + EXPECT_EQ(nullptr, scene); +} + TEST_F(utObjImportExport, 0based_array_Test) { static const std::string ObjModel = "v -0.500000 0.000000 0.400000\n" From 606fd6b1a1e4b1726981ca9f0b4a87458e474f3f Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Fri, 29 Dec 2017 18:07:41 +0100 Subject: [PATCH 481/490] Replace assertion by an exception in Obj parsing This crash could happen when the user try to parse an ill-formed obj file. --- code/ObjFileParser.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/ObjFileParser.cpp b/code/ObjFileParser.cpp index d89d52977..a8d92497d 100644 --- a/code/ObjFileParser.cpp +++ b/code/ObjFileParser.cpp @@ -353,7 +353,8 @@ void ObjFileParser::getHomogeneousVector3( std::vector &point3d_arra copyNextWord( m_buffer, Buffersize ); w = ( ai_real ) fast_atof( m_buffer ); - ai_assert( w != 0 ); + if (w == 0) + throw DeadlyImportError("OBJ: Invalid component in homogeneous vector (Division by zero)"); point3d_array.push_back( aiVector3D( x/w, y/w, z/w ) ); m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); From d3150eedfb99ce97dd243e14894c98a7936cfc7c Mon Sep 17 00:00:00 2001 From: Robert Spencer Date: Fri, 29 Dec 2017 19:46:16 +0200 Subject: [PATCH 482/490] Version bump pyassimp --- port/PyAssimp/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/PyAssimp/setup.py b/port/PyAssimp/setup.py index 26cf9b400..4ccfaf116 100644 --- a/port/PyAssimp/setup.py +++ b/port/PyAssimp/setup.py @@ -3,7 +3,7 @@ import os from distutils.core import setup setup(name='pyassimp', - version='3.3', + version='4.1.0', license='ISC', description='Python bindings for the Open Asset Import Library (ASSIMP)', url='https://github.com/assimp/assimp', From ced6e8ce43f0efd5af30032e4dfbccdd973472df Mon Sep 17 00:00:00 2001 From: Alexandre Avenel Date: Sat, 30 Dec 2017 13:22:30 +0100 Subject: [PATCH 483/490] Use std::unique_ptr in ObjFileParser --- code/ObjFileParser.cpp | 14 +++++--------- code/ObjFileParser.h | 3 ++- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/code/ObjFileParser.cpp b/code/ObjFileParser.cpp index a8d92497d..2b9873bc1 100644 --- a/code/ObjFileParser.cpp +++ b/code/ObjFileParser.cpp @@ -60,7 +60,7 @@ const std::string ObjFileParser::DEFAULT_MATERIAL = AI_DEFAULT_MATERIAL_NAME; ObjFileParser::ObjFileParser() : m_DataIt() , m_DataItEnd() -, m_pModel( NULL ) +, m_pModel( nullptr ) , m_uiLine( 0 ) , m_pIO( nullptr ) , m_progress( nullptr ) @@ -73,7 +73,7 @@ ObjFileParser::ObjFileParser( IOStreamBuffer &streamBuffer, const std::str const std::string &originalObjFileName) : m_DataIt(), m_DataItEnd(), - m_pModel(NULL), + m_pModel(nullptr), m_uiLine(0), m_pIO( io ), m_progress(progress), @@ -82,7 +82,7 @@ ObjFileParser::ObjFileParser( IOStreamBuffer &streamBuffer, const std::str std::fill_n(m_buffer,Buffersize,0); // Create the model instance to store all the data - m_pModel = new ObjFile::Model(); + m_pModel.reset(new ObjFile::Model()); m_pModel->m_ModelName = modelName; // create default material and store it @@ -96,8 +96,6 @@ ObjFileParser::ObjFileParser( IOStreamBuffer &streamBuffer, const std::str } ObjFileParser::~ObjFileParser() { - delete m_pModel; - m_pModel = NULL; } void ObjFileParser::setBuffer( std::vector &buffer ) { @@ -106,7 +104,7 @@ void ObjFileParser::setBuffer( std::vector &buffer ) { } ObjFile::Model *ObjFileParser::GetModel() const { - return m_pModel; + return m_pModel.get(); } void ObjFileParser::parseFile( IOStreamBuffer &streamBuffer ) { @@ -479,8 +477,6 @@ void ObjFileParser::getFace( aiPrimitiveType type ) { } else { //On error, std::atoi will return 0 which is not a valid value delete face; - delete m_pModel; - m_pModel = nullptr; throw DeadlyImportError("OBJ: Invalid face indice"); } @@ -642,7 +638,7 @@ void ObjFileParser::getMaterialLib() { m_pIO->Close( pFile ); // Importing the material library - ObjFileMtlImporter mtlImporter( buffer, strMatName, m_pModel ); + ObjFileMtlImporter mtlImporter( buffer, strMatName, m_pModel.get() ); } // ------------------------------------------------------------------- diff --git a/code/ObjFileParser.h b/code/ObjFileParser.h index fa5b3ca31..15332a1df 100644 --- a/code/ObjFileParser.h +++ b/code/ObjFileParser.h @@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include #include #include @@ -145,7 +146,7 @@ private: //! Iterator to end position of buffer DataArrayIt m_DataItEnd; //! Pointer to model instance - ObjFile::Model *m_pModel; + std::unique_ptr m_pModel; //! Current line (for debugging) unsigned int m_uiLine; //! Helper buffer From fb660823fc5e9fd4fc3d866f91d2119eebed3d3c Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 1 Jan 2018 19:42:01 +0100 Subject: [PATCH 484/490] new gtest version: 1.8.0 --- contrib/zip/src/miniz.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/zip/src/miniz.h b/contrib/zip/src/miniz.h index 916fb1ff8..935f7de2e 100644 --- a/contrib/zip/src/miniz.h +++ b/contrib/zip/src/miniz.h @@ -4427,7 +4427,7 @@ mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; MZ_FILE *pSrc_file = NULL; - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || ( level_and_flags > MZ_UBER_COMPRESSION)) return MZ_FALSE; local_dir_header_ofs = cur_archive_file_ofs = pZip->m_archive_size; From bd8186979444cb8881aa4bf6fc127ec846b99e8c Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 1 Jan 2018 20:57:59 +0100 Subject: [PATCH 485/490] Fix some minor findings. --- include/assimp/vector3.inl | 143 +++++++++++++++++++++++++------------ 1 file changed, 96 insertions(+), 47 deletions(-) diff --git a/include/assimp/vector3.inl b/include/assimp/vector3.inl index 2b132c1a5..dc2676a40 100644 --- a/include/assimp/vector3.inl +++ b/include/assimp/vector3.inl @@ -55,8 +55,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // ------------------------------------------------------------------------------------------------ /** Transformation of a vector by a 3x3 matrix */ template -inline aiVector3t operator * (const aiMatrix3x3t& pMatrix, const aiVector3t& pVector) -{ +AI_FORCE_INLINE +aiVector3t operator * (const aiMatrix3x3t& pMatrix, const aiVector3t& pVector) { aiVector3t res; res.x = pMatrix.a1 * pVector.x + pMatrix.a2 * pVector.y + pMatrix.a3 * pVector.z; res.y = pMatrix.b1 * pVector.x + pMatrix.b2 * pVector.y + pMatrix.b3 * pVector.z; @@ -67,8 +67,8 @@ inline aiVector3t operator * (const aiMatrix3x3t& pMatrix, const a // ------------------------------------------------------------------------------------------------ /** Transformation of a vector by a 4x4 matrix */ template -inline aiVector3t operator * (const aiMatrix4x4t& pMatrix, const aiVector3t& pVector) -{ +AI_FORCE_INLINE +aiVector3t operator * (const aiMatrix4x4t& pMatrix, const aiVector3t& pVector) { aiVector3t res; res.x = pMatrix.a1 * pVector.x + pMatrix.a2 * pVector.y + pMatrix.a3 * pVector.z + pMatrix.a4; res.y = pMatrix.b1 * pVector.x + pMatrix.b2 * pVector.y + pMatrix.b3 * pVector.z + pMatrix.b4; @@ -83,65 +83,99 @@ aiVector3t::operator aiVector3t () const { } // ------------------------------------------------------------------------------------------------ template -AI_FORCE_INLINE void aiVector3t::Set( TReal pX, TReal pY, TReal pZ) { - x = pX; y = pY; z = pZ; +AI_FORCE_INLINE +void aiVector3t::Set( TReal pX, TReal pY, TReal pZ) { + x = pX; + y = pY; + z = pZ; } // ------------------------------------------------------------------------------------------------ template -AI_FORCE_INLINE TReal aiVector3t::SquareLength() const { +AI_FORCE_INLINE +TReal aiVector3t::SquareLength() const { return x*x + y*y + z*z; } // ------------------------------------------------------------------------------------------------ template -AI_FORCE_INLINE TReal aiVector3t::Length() const { +AI_FORCE_INLINE +TReal aiVector3t::Length() const { return std::sqrt( SquareLength()); } // ------------------------------------------------------------------------------------------------ template -AI_FORCE_INLINE aiVector3t& aiVector3t::Normalize() { - *this /= Length(); return *this; -} -// ------------------------------------------------------------------------------------------------ -template -AI_FORCE_INLINE aiVector3t& aiVector3t::NormalizeSafe() { - TReal len = Length(); - if (len > static_cast(0)) - *this /= len; +AI_FORCE_INLINE +aiVector3t& aiVector3t::Normalize() { + *this /= Length(); + return *this; } // ------------------------------------------------------------------------------------------------ template -AI_FORCE_INLINE const aiVector3t& aiVector3t::operator += (const aiVector3t& o) { - x += o.x; y += o.y; z += o.z; return *this; +AI_FORCE_INLINE +aiVector3t& aiVector3t::NormalizeSafe() { + TReal len = Length(); + if ( len > static_cast< TReal >( 0 ) ) { + *this /= len; + } + return *this; } // ------------------------------------------------------------------------------------------------ template -AI_FORCE_INLINE const aiVector3t& aiVector3t::operator -= (const aiVector3t& o) { - x -= o.x; y -= o.y; z -= o.z; return *this; +AI_FORCE_INLINE +const aiVector3t& aiVector3t::operator += (const aiVector3t& o) { + x += o.x; + y += o.y; + z += o.z; + + return *this; } // ------------------------------------------------------------------------------------------------ template -AI_FORCE_INLINE const aiVector3t& aiVector3t::operator *= (TReal f) { - x *= f; y *= f; z *= f; return *this; +AI_FORCE_INLINE +const aiVector3t& aiVector3t::operator -= (const aiVector3t& o) { + x -= o.x; + y -= o.y; + z -= o.z; + + return *this; } // ------------------------------------------------------------------------------------------------ template -AI_FORCE_INLINE const aiVector3t& aiVector3t::operator /= (TReal f) { - x /= f; y /= f; z /= f; return *this; +AI_FORCE_INLINE +const aiVector3t& aiVector3t::operator *= (TReal f) { + x *= f; + y *= f; + z *= f; + + return *this; } // ------------------------------------------------------------------------------------------------ template -AI_FORCE_INLINE aiVector3t& aiVector3t::operator *= (const aiMatrix3x3t& mat){ - return(*this = mat * (*this)); +AI_FORCE_INLINE +const aiVector3t& aiVector3t::operator /= (TReal f) { + const TReal invF = (TReal) 1.0 / f; + x *= invF; + y *= invF; + z *= invF; + + return *this; } // ------------------------------------------------------------------------------------------------ template -AI_FORCE_INLINE aiVector3t& aiVector3t::operator *= (const aiMatrix4x4t& mat){ - return(*this = mat * (*this)); +AI_FORCE_INLINE +aiVector3t& aiVector3t::operator *= (const aiMatrix3x3t& mat){ + return (*this = mat * (*this)); } // ------------------------------------------------------------------------------------------------ template -AI_FORCE_INLINE TReal aiVector3t::operator[](unsigned int i) const { +AI_FORCE_INLINE +aiVector3t& aiVector3t::operator *= (const aiMatrix4x4t& mat){ + return (*this = mat * (*this)); +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE +TReal aiVector3t::operator[](unsigned int i) const { switch (i) { case 0: return x; @@ -156,7 +190,8 @@ AI_FORCE_INLINE TReal aiVector3t::operator[](unsigned int i) const { } // ------------------------------------------------------------------------------------------------ template -AI_FORCE_INLINE TReal& aiVector3t::operator[](unsigned int i) { +AI_FORCE_INLINE +TReal& aiVector3t::operator[](unsigned int i) { // return *(&x + i); switch (i) { case 0: @@ -172,17 +207,20 @@ AI_FORCE_INLINE TReal& aiVector3t::operator[](unsigned int i) { } // ------------------------------------------------------------------------------------------------ template -AI_FORCE_INLINE bool aiVector3t::operator== (const aiVector3t& other) const { +AI_FORCE_INLINE +bool aiVector3t::operator== (const aiVector3t& other) const { return x == other.x && y == other.y && z == other.z; } // ------------------------------------------------------------------------------------------------ template -AI_FORCE_INLINE bool aiVector3t::operator!= (const aiVector3t& other) const { +AI_FORCE_INLINE +bool aiVector3t::operator!= (const aiVector3t& other) const { return x != other.x || y != other.y || z != other.z; } // --------------------------------------------------------------------------- template -AI_FORCE_INLINE bool aiVector3t::Equal(const aiVector3t& other, TReal epsilon) const { +AI_FORCE_INLINE +bool aiVector3t::Equal(const aiVector3t& other, TReal epsilon) const { return std::abs(x - other.x) <= epsilon && std::abs(y - other.y) <= epsilon && @@ -190,66 +228,77 @@ AI_FORCE_INLINE bool aiVector3t::Equal(const aiVector3t& other, TR } // ------------------------------------------------------------------------------------------------ template -AI_FORCE_INLINE bool aiVector3t::operator < (const aiVector3t& other) const { +AI_FORCE_INLINE +bool aiVector3t::operator < (const aiVector3t& other) const { return x != other.x ? x < other.x : y != other.y ? y < other.y : z < other.z; } // ------------------------------------------------------------------------------------------------ template -AI_FORCE_INLINE const aiVector3t aiVector3t::SymMul(const aiVector3t& o) { +AI_FORCE_INLINE +const aiVector3t aiVector3t::SymMul(const aiVector3t& o) { return aiVector3t(x*o.x,y*o.y,z*o.z); } // ------------------------------------------------------------------------------------------------ // symmetric addition template -AI_FORCE_INLINE aiVector3t operator + (const aiVector3t& v1, const aiVector3t& v2) { +AI_FORCE_INLINE +aiVector3t operator + (const aiVector3t& v1, const aiVector3t& v2) { return aiVector3t( v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); } // ------------------------------------------------------------------------------------------------ // symmetric subtraction template -AI_FORCE_INLINE aiVector3t operator - (const aiVector3t& v1, const aiVector3t& v2) { +AI_FORCE_INLINE +aiVector3t operator - (const aiVector3t& v1, const aiVector3t& v2) { return aiVector3t( v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); } // ------------------------------------------------------------------------------------------------ // scalar product template -AI_FORCE_INLINE TReal operator * (const aiVector3t& v1, const aiVector3t& v2) { +AI_FORCE_INLINE +TReal operator * (const aiVector3t& v1, const aiVector3t& v2) { return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; } // ------------------------------------------------------------------------------------------------ // scalar multiplication template -AI_FORCE_INLINE aiVector3t operator * ( TReal f, const aiVector3t& v) { +AI_FORCE_INLINE +aiVector3t operator * ( TReal f, const aiVector3t& v) { return aiVector3t( f*v.x, f*v.y, f*v.z); } // ------------------------------------------------------------------------------------------------ // and the other way around template -AI_FORCE_INLINE aiVector3t operator * ( const aiVector3t& v, TReal f) { +AI_FORCE_INLINE +aiVector3t operator * ( const aiVector3t& v, TReal f) { return aiVector3t( f*v.x, f*v.y, f*v.z); } // ------------------------------------------------------------------------------------------------ // scalar division template -AI_FORCE_INLINE aiVector3t operator / ( const aiVector3t& v, TReal f) { +AI_FORCE_INLINE +aiVector3t operator / ( const aiVector3t& v, TReal f) { return v * (1/f); } // ------------------------------------------------------------------------------------------------ // vector division template -AI_FORCE_INLINE aiVector3t operator / ( const aiVector3t& v, const aiVector3t& v2) { +AI_FORCE_INLINE +aiVector3t operator / ( const aiVector3t& v, const aiVector3t& v2) { return aiVector3t(v.x / v2.x,v.y / v2.y,v.z / v2.z); } // ------------------------------------------------------------------------------------------------ // cross product -template -AI_FORCE_INLINE aiVector3t operator ^ ( const aiVector3t& v1, const aiVector3t& v2) { +template +AI_FORCE_INLINE +aiVector3t operator ^ ( const aiVector3t& v1, const aiVector3t& v2) { return aiVector3t( v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x); } // ------------------------------------------------------------------------------------------------ // vector negation -template -AI_FORCE_INLINE aiVector3t operator - ( const aiVector3t& v) { +template +AI_FORCE_INLINE +aiVector3t operator - ( const aiVector3t& v) { return aiVector3t( -v.x, -v.y, -v.z); } From d308cfcb43029dad192feee29d08eec65504ede0 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 2 Jan 2018 19:23:21 +0200 Subject: [PATCH 486/490] glTF: Fix delete / delete[] mismatch --- code/glTFAsset.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/glTFAsset.inl b/code/glTFAsset.inl index a5d74f5a0..ee4f93e2f 100644 --- a/code/glTFAsset.inl +++ b/code/glTFAsset.inl @@ -297,7 +297,7 @@ inline void Buffer::Read(Value& obj, Asset& r) if (dataURI.base64) { uint8_t* data = 0; this->byteLength = Util::DecodeBase64(dataURI.data, dataURI.dataLength, data); - this->mData.reset(data); + this->mData.reset(data, std::default_delete()); if (statedLength > 0 && this->byteLength != statedLength) { throw DeadlyImportError("GLTF: buffer \"" + id + "\", expected " + to_string(statedLength) + From 7ebd8e7543aa6c0cd0b8a6864d4cff4a863990bb Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 2 Jan 2018 20:09:22 +0200 Subject: [PATCH 487/490] glTFAsset: Use std:unique_ptr for Image data --- code/glTFAsset.h | 4 ++-- code/glTFAsset.inl | 15 +++++++-------- code/glTFImporter.cpp | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/code/glTFAsset.h b/code/glTFAsset.h index 0c3de16a5..e1daf27a2 100644 --- a/code/glTFAsset.h +++ b/code/glTFAsset.h @@ -649,7 +649,7 @@ namespace glTF int width, height; private: - uint8_t* mData; + std::unique_ptr mData; size_t mDataLength; public: @@ -664,7 +664,7 @@ namespace glTF { return mDataLength; } inline const uint8_t* GetData() const - { return mData; } + { return mData.get(); } inline uint8_t* StealData(); diff --git a/code/glTFAsset.inl b/code/glTFAsset.inl index ee4f93e2f..b01957f3b 100644 --- a/code/glTFAsset.inl +++ b/code/glTFAsset.inl @@ -601,7 +601,6 @@ T Accessor::Indexer::GetValue(int i) inline Image::Image() : width(0) , height(0) - , mData(0) , mDataLength(0) { @@ -624,8 +623,8 @@ inline void Image::Read(Value& obj, Asset& r) Ref bv = r.bufferViews.Get(bufferViewId); if (bv) { mDataLength = bv->byteLength; - mData = new uint8_t[mDataLength]; - memcpy(mData, bv->buffer->GetPointer() + bv->byteOffset, mDataLength); + mData.reset(new uint8_t[mDataLength]); + memcpy(mData.get(), bv->buffer->GetPointer() + bv->byteOffset, mDataLength); } } } @@ -640,7 +639,9 @@ inline void Image::Read(Value& obj, Asset& r) if (ParseDataURI(uristr, uri->GetStringLength(), dataURI)) { mimeType = dataURI.mediaType; if (dataURI.base64) { - mDataLength = Util::DecodeBase64(dataURI.data, dataURI.dataLength, mData); + uint8_t *ptr = nullptr; + mDataLength = Util::DecodeBase64(dataURI.data, dataURI.dataLength, ptr); + mData.reset(ptr); } } else { @@ -652,10 +653,8 @@ inline void Image::Read(Value& obj, Asset& r) inline uint8_t* Image::StealData() { - uint8_t* data = mData; mDataLength = 0; - mData = 0; - return data; + return mData.release(); } inline void Image::SetData(uint8_t* data, size_t length, Asset& r) @@ -670,7 +669,7 @@ inline void Image::SetData(uint8_t* data, size_t length, Asset& r) bufferView->byteOffset = b->AppendData(data, length); } else { // text file: will be stored as a data uri - this->mData = data; + this->mData.reset(data); this->mDataLength = length; } } diff --git a/code/glTFImporter.cpp b/code/glTFImporter.cpp index 5d1b5afab..381e459fd 100644 --- a/code/glTFImporter.cpp +++ b/code/glTFImporter.cpp @@ -616,7 +616,7 @@ void glTFImporter::ImportEmbeddedTextures(glTF::Asset& r) // Add the embedded textures for (size_t i = 0; i < r.images.Size(); ++i) { - Image img = r.images[i]; + Image &img = r.images[i]; if (!img.HasData()) continue; int idx = mScene->mNumTextures++; From aac41cf2c3565d0f7697c475b5f48ee5c4331569 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 2 Jan 2018 20:28:08 +0200 Subject: [PATCH 488/490] MMD: Fix delete / delete[] mismatch Also this was a horrible abuse of std::vector and shouldn't have worked --- code/MMDImporter.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/code/MMDImporter.cpp b/code/MMDImporter.cpp index c813063ab..5d4bd137a 100644 --- a/code/MMDImporter.cpp +++ b/code/MMDImporter.cpp @@ -324,8 +324,10 @@ aiMesh *MMDImporter::CreateMesh(const pmx::PmxModel *pModel, auto it = bone_vertex_map.find(ii); if (it != bone_vertex_map.end()) { pBone->mNumWeights = static_cast(it->second.size()); - pBone->mWeights = it->second.data(); - it->second.swap(*(new vector)); + pBone->mWeights = new aiVertexWeight[pBone->mNumWeights]; + for (unsigned int j = 0; j < pBone->mNumWeights; j++) { + pBone->mWeights[j] = it->second[j]; + } } bone_ptr_ptr[ii] = pBone; } From 8a505398a3a735763a1babbc7e08ae14d0eafd20 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 2 Jan 2018 20:35:06 +0200 Subject: [PATCH 489/490] MMD: Fix memory leak --- code/MMDImporter.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/MMDImporter.cpp b/code/MMDImporter.cpp index 5d4bd137a..76ad9115c 100644 --- a/code/MMDImporter.cpp +++ b/code/MMDImporter.cpp @@ -168,7 +168,7 @@ void MMDImporter::CreateDataFromImport(const pmx::PmxModel *pModel, } // create node hierarchy for bone position - aiNode **ppNode = new aiNode *[pModel->bone_count]; + std::unique_ptr ppNode(new aiNode *[pModel->bone_count]); for (auto i = 0; i < pModel->bone_count; i++) { ppNode[i] = new aiNode(pModel->bones[i].bone_name); } @@ -177,9 +177,9 @@ void MMDImporter::CreateDataFromImport(const pmx::PmxModel *pModel, const pmx::PmxBone &bone = pModel->bones[i]; if (bone.parent_index < 0) { - pScene->mRootNode->addChildren(1, ppNode + i); + pScene->mRootNode->addChildren(1, ppNode.get() + i); } else { - ppNode[bone.parent_index]->addChildren(1, ppNode + i); + ppNode[bone.parent_index]->addChildren(1, ppNode.get() + i); aiVector3D v3 = aiVector3D( bone.position[0] - pModel->bones[bone.parent_index].position[0], From cf59c7d1533ea712306e9ffc966bcde18dca704d Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 3 Jan 2018 00:56:32 +0100 Subject: [PATCH 490/490] Update o3dgcTimer.h closes https://github.com/assimp/assimp/issues/1642: fix build on Hurd --- contrib/Open3DGC/o3dgcTimer.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/Open3DGC/o3dgcTimer.h b/contrib/Open3DGC/o3dgcTimer.h index 00fe5b653..d9a285968 100644 --- a/contrib/Open3DGC/o3dgcTimer.h +++ b/contrib/Open3DGC/o3dgcTimer.h @@ -30,7 +30,7 @@ THE SOFTWARE. /* Thank you, Microsoft, for file WinDef.h with min/max redefinition. */ #define NOMINMAX #include -#elif __MACH__ +#elif __APPLE__ #include #include #else @@ -73,7 +73,7 @@ namespace o3dgc LARGE_INTEGER m_freq; }; -#elif __MACH__ +#elif __APPLE__ class Timer { public:

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