From 858a9c0e1b8bf7da747a01b44f0f7d319800087f Mon Sep 17 00:00:00 2001 From: David Williams Date: Tue, 1 Jan 2013 14:09:40 +0000 Subject: [PATCH 01/38] Replaced some assert()s with POLYVOX_ASSERT()s. --- .../include/PolyVoxCore/AStarPathfinder.inl | 10 ++++--- .../AmbientOcclusionCalculator.inl | 8 ++--- .../PolyVoxCore/include/PolyVoxCore/Array.inl | 20 ++++++------- .../include/PolyVoxCore/Impl/Block.inl | 29 ++++++++++--------- .../include/PolyVoxCore/Impl/SubArray.inl | 10 +++---- 5 files changed, 40 insertions(+), 37 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/AStarPathfinder.inl b/library/PolyVoxCore/include/PolyVoxCore/AStarPathfinder.inl index 26c5c398..c8fce4fb 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/AStarPathfinder.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/AStarPathfinder.inl @@ -21,6 +21,8 @@ freely, subject to the following restrictions: distribution. *******************************************************************************/ +#include "PolyVoxCore/Impl/ErrorHandling.h" + namespace PolyVox { //////////////////////////////////////////////////////////////////////////////// @@ -296,14 +298,14 @@ namespace PolyVox hVal = SixConnectedCost(a, b); break; default: - assert(false); //Invalid case. + POLYVOX_ASSERT(false, "Invalid case"); } //Sanity checks in debug mode. These can come out eventually, but I //want to make sure that the heuristics I've come up with make sense. - assert((a-b).length() <= TwentySixConnectedCost(a,b)); - assert(TwentySixConnectedCost(a,b) <= EighteenConnectedCost(a,b)); - assert(EighteenConnectedCost(a,b) <= SixConnectedCost(a,b)); + POLYVOX_ASSERT((a-b).length() <= TwentySixConnectedCost(a,b), "A* heuristic error."); + POLYVOX_ASSERT(TwentySixConnectedCost(a,b) <= EighteenConnectedCost(a,b), "A* heuristic error."); + POLYVOX_ASSERT(EighteenConnectedCost(a,b) <= SixConnectedCost(a,b), "A* heuristic error."); //Apply the bias to the computed h value; hVal *= m_params.hBias; diff --git a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl index 1c3c9727..95c1410e 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl @@ -39,9 +39,9 @@ namespace PolyVox uint16_t uIndexIncreament; //Make sure that the size of the volume is an exact multiple of the size of the array. - assert(volInput->getWidth() % arrayResult->getDimension(0) == 0); - assert(volInput->getHeight() % arrayResult->getDimension(1) == 0); - assert(volInput->getDepth() % arrayResult->getDimension(2) == 0); + POLYVOX_ASSERT(volInput->getWidth() % arrayResult->getDimension(0) == 0, "Volume width must be an exact multiple of array width."); + POLYVOX_ASSERT(volInput->getHeight() % arrayResult->getDimension(1) == 0, "Volume height must be an exact multiple of array height."); + POLYVOX_ASSERT(volInput->getDepth() % arrayResult->getDimension(2) == 0, "Volume depth must be an exact multiple of array depth."); //Our initial indices. It doesn't matter exactly what we set here, but the code below makes //sure they are different for different regions which helps reduce tiling patterns in the results. @@ -116,7 +116,7 @@ namespace PolyVox else { fVisibility = static_cast(uVisibleDirections) / static_cast(uNoOfSamplesPerOutputElement); - assert((fVisibility >= 0.0f) && (fVisibility <= 1.0f)); + POLYVOX_ASSERT((fVisibility >= 0.0f) && (fVisibility <= 1.0f), "Visibility value out of range."); } (*arrayResult)[z / iRatioZ][y / iRatioY][x / iRatioX] = static_cast(255.0f * fVisibility); diff --git a/library/PolyVoxCore/include/PolyVoxCore/Array.inl b/library/PolyVoxCore/include/PolyVoxCore/Array.inl index 39d59701..366180c7 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Array.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Array.inl @@ -73,7 +73,7 @@ namespace PolyVox template SubArray Array::operator[](uint32_t uIndex) { - assert(uIndex(&m_pElements[uIndex*m_pOffsets[0]], m_pDimensions+1, m_pOffsets+1); @@ -91,7 +91,7 @@ namespace PolyVox template const SubArray Array::operator[](uint32_t uIndex) const { - assert(uIndex(&m_pElements[uIndex*m_pOffsets[0]], m_pDimensions+1, m_pOffsets+1); @@ -139,7 +139,7 @@ namespace PolyVox m_uNoOfElements = 1; for (uint32_t i = 0; i uint32_t Array::getDimension(uint32_t uDimension) { - assert(uDimension < noOfDims); + POLYVOX_ASSERT(uDimension < noOfDims, "Dimension out of range"); return m_pDimensions[uDimension]; } @@ -198,14 +198,14 @@ namespace PolyVox ,m_uNoOfElements(0) { //Not implemented - assert(false); + POLYVOX_ASSERT(false, "Not implemented."); } template Array& Array::operator=(const Array& rhs) { //Not implemented - assert(false); + POLYVOX_ASSERT(false, "Not implemented."); return *this; } @@ -251,14 +251,14 @@ namespace PolyVox template ElementType& Array<1, ElementType>::operator[] (uint32_t uIndex) { - assert(uIndex const ElementType& Array<1, ElementType>::operator[] (uint32_t uIndex) const { - assert(uIndex Array<1, ElementType>& Array<1, ElementType>::operator=(const Array<1, ElementType>& rhs) { //Not implemented - assert(false); + POLYVOX_ASSERT(false, "Not implemented."); return *this; } diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl index 8927816c..226e7ded 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl @@ -24,7 +24,8 @@ freely, subject to the following restrictions: #include "PolyVoxCore/Impl/Utility.h" #include "PolyVoxCore/Vector.h" -#include +#include "PolyVoxCore/Impl/ErrorHandling.h" + #include //For memcpy #include #include //for std::invalid_argument @@ -54,11 +55,11 @@ namespace PolyVox template VoxelType Block::getVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const { - assert(uXPos < m_uSideLength); - assert(uYPos < m_uSideLength); - assert(uZPos < m_uSideLength); + POLYVOX_ASSERT(uXPos < m_uSideLength, "Supplied position is outside of the block"); + POLYVOX_ASSERT(uYPos < m_uSideLength, "Supplied position is outside of the block"); + POLYVOX_ASSERT(uZPos < m_uSideLength, "Supplied position is outside of the block"); - assert(m_tUncompressedData); + POLYVOX_ASSERT(m_tUncompressedData, "No uncompressed data - block must be decompressed before accessing voxels."); return m_tUncompressedData [ @@ -77,11 +78,11 @@ namespace PolyVox template void Block::setVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos, VoxelType tValue) { - assert(uXPos < m_uSideLength); - assert(uYPos < m_uSideLength); - assert(uZPos < m_uSideLength); + POLYVOX_ASSERT(uXPos < m_uSideLength, "Supplied position is outside of the block"); + POLYVOX_ASSERT(uYPos < m_uSideLength, "Supplied position is outside of the block"); + POLYVOX_ASSERT(uZPos < m_uSideLength, "Supplied position is outside of the block"); - assert(m_tUncompressedData); + POLYVOX_ASSERT(m_tUncompressedData, "No uncompressed data - block must be decompressed before accessing voxels."); m_tUncompressedData [ @@ -125,7 +126,7 @@ namespace PolyVox void Block::initialise(uint16_t uSideLength) { //Debug mode validation - assert(isPowerOf2(uSideLength)); + POLYVOX_ASSERT(isPowerOf2(uSideLength), "Block side length must be a power of two."); //Release mode validation if(!isPowerOf2(uSideLength)) @@ -151,8 +152,8 @@ namespace PolyVox template void Block::compress(void) { - assert(m_bIsCompressed == false); - assert(m_tUncompressedData != 0); + POLYVOX_ASSERT(m_bIsCompressed == false, "Attempted to compress block which is already flagged as compressed."); + POLYVOX_ASSERT(m_tUncompressedData != 0, "No uncompressed data is present."); //If the uncompressed data hasn't actually been //modified then we don't need to redo the compression. @@ -197,8 +198,8 @@ namespace PolyVox template void Block::uncompress(void) { - assert(m_bIsCompressed == true); - assert(m_tUncompressedData == 0); + POLYVOX_ASSERT(m_bIsCompressed == true, "Attempted to uncompress block which is not flagged as compressed."); + POLYVOX_ASSERT(m_tUncompressedData == 0, "Uncompressed data already exists."); m_tUncompressedData = new VoxelType[m_uSideLength * m_uSideLength * m_uSideLength]; VoxelType* pUncompressedData = m_tUncompressedData; diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/SubArray.inl b/library/PolyVoxCore/include/PolyVoxCore/Impl/SubArray.inl index 49ae1ea0..c1285d60 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/SubArray.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/SubArray.inl @@ -21,14 +21,14 @@ misrepresented as being the original software. distribution. *******************************************************************************/ -#include +#include "PolyVoxCore/Impl/ErrorHandling.h" namespace PolyVox { template SubArray SubArray::operator[](uint32_t uIndex) { - assert(uIndex(&m_pElements[uIndex*m_pOffsets[0]], m_pDimensions+1, m_pOffsets+1); @@ -37,7 +37,7 @@ namespace PolyVox template const SubArray SubArray::operator[](uint32_t uIndex) const { - assert(uIndex(&m_pElements[uIndex*m_pOffsets[0]], m_pDimensions+1, m_pOffsets+1); @@ -56,14 +56,14 @@ namespace PolyVox template ElementType& SubArray<1, ElementType>::operator[] (uint32_t uIndex) { - assert(uIndex const ElementType& SubArray<1, ElementType>::operator[] (uint32_t uIndex) const { - assert(uIndex Date: Tue, 1 Jan 2013 14:50:58 +0000 Subject: [PATCH 02/38] More replacing assert() with POLYVOX_ASSERT. --- .../include/PolyVoxCore/BaseVolume.inl | 22 +++++++++---------- .../include/PolyVoxCore/BaseVolumeSampler.inl | 2 +- .../include/PolyVoxCore/ConstVolumeProxy.h | 8 +++---- .../PolyVoxCore/CubicSurfaceExtractor.inl | 2 +- .../include/PolyVoxCore/Interpolation.h | 10 ++++----- .../include/PolyVoxCore/LargeVolume.inl | 19 ++++++++-------- .../PolyVoxCore/LargeVolumeSampler.inl | 2 +- 7 files changed, 33 insertions(+), 32 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.inl b/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.inl index 7d5d48fa..77949413 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.inl @@ -38,14 +38,14 @@ namespace PolyVox //////////////////////////////////////////////////////////////////////////////// /// This function should never be called. Copying volumes by value would be expensive, and we want to prevent users from doing /// it by accident (such as when passing them as paramenters to functions). That said, there are times when you really do want to - /// make a copy of a volume and in this case you should look at the Volumeresampler. + /// make a copy of a volume and in this case you should look at the VolumeResampler. /// /// \sa VolumeResampler //////////////////////////////////////////////////////////////////////////////// template BaseVolume::BaseVolume(const BaseVolume& /*rhs*/) { - assert(false); // See function comment above. + POLYVOX_ASSERT(false, "Copy constructor not implemented."); // See function comment above. } //////////////////////////////////////////////////////////////////////////////// @@ -66,7 +66,7 @@ namespace PolyVox template BaseVolume& BaseVolume::operator=(const BaseVolume& /*rhs*/) { - assert(false); // See function comment above. + POLYVOX_ASSERT(false, "Assignment operator not implemented."); // See function comment above. } //////////////////////////////////////////////////////////////////////////////// @@ -162,7 +162,7 @@ namespace PolyVox template VoxelType BaseVolume::getVoxel(int32_t /*uXPos*/, int32_t /*uYPos*/, int32_t /*uZPos*/) const { - assert(false); + POLYVOX_ASSERT(false, "You should never call the base class version of this function."); return VoxelType(); } @@ -173,7 +173,7 @@ namespace PolyVox template VoxelType BaseVolume::getVoxel(const Vector3DInt32& /*v3dPos*/) const { - assert(false); + POLYVOX_ASSERT(false, "You should never call the base class version of this function."); return VoxelType(); } @@ -186,7 +186,7 @@ namespace PolyVox template VoxelType BaseVolume::getVoxelAt(int32_t /*uXPos*/, int32_t /*uYPos*/, int32_t /*uZPos*/) const { - assert(false); + POLYVOX_ASSERT(false, "You should never call the base class version of this function."); return VoxelType(); } @@ -197,7 +197,7 @@ namespace PolyVox template VoxelType BaseVolume::getVoxelAt(const Vector3DInt32& /*v3dPos*/) const { - assert(false); + POLYVOX_ASSERT(false, "You should never call the base class version of this function."); return VoxelType(); } @@ -210,7 +210,7 @@ namespace PolyVox template VoxelType BaseVolume::getVoxelWithWrapping(int32_t /*uXPos*/, int32_t /*uYPos*/, int32_t /*uZPos*/, WrapMode /*eWrapMode*/, VoxelType /*tBorder*/) const { - assert(false); + POLYVOX_ASSERT(false, "You should never call the base class version of this function."); return VoxelType(); } @@ -221,7 +221,7 @@ namespace PolyVox template VoxelType BaseVolume::getVoxelWithWrapping(const Vector3DInt32& /*v3dPos*/, WrapMode /*eWrapMode*/, VoxelType /*tBorder*/) const { - assert(false); + POLYVOX_ASSERT(false, "You should never call the base class version of this function."); return VoxelType(); } @@ -244,7 +244,7 @@ namespace PolyVox template bool BaseVolume::setVoxelAt(int32_t /*uXPos*/, int32_t /*uYPos*/, int32_t /*uZPos*/, VoxelType /*tValue*/) { - assert(false); + POLYVOX_ASSERT(false, "You should never call the base class version of this function."); return false; } @@ -256,7 +256,7 @@ namespace PolyVox template bool BaseVolume::setVoxelAt(const Vector3DInt32& /*v3dPos*/, VoxelType /*tValue*/) { - assert(false); + POLYVOX_ASSERT(false, "You should never call the base class version of this function."); return false; } diff --git a/library/PolyVoxCore/include/PolyVoxCore/BaseVolumeSampler.inl b/library/PolyVoxCore/include/PolyVoxCore/BaseVolumeSampler.inl index fb7fe6b1..ef136db8 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/BaseVolumeSampler.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/BaseVolumeSampler.inl @@ -375,7 +375,7 @@ namespace PolyVox default: { //Should never happen - assert(false); + POLYVOX_ASSERT(false, "Invalid case."); return VoxelType(0); } } diff --git a/library/PolyVoxCore/include/PolyVoxCore/ConstVolumeProxy.h b/library/PolyVoxCore/include/PolyVoxCore/ConstVolumeProxy.h index e778bbcf..63cc16ca 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/ConstVolumeProxy.h +++ b/library/PolyVoxCore/include/PolyVoxCore/ConstVolumeProxy.h @@ -37,25 +37,25 @@ namespace PolyVox public: VoxelType getVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos) const { - assert(m_regValid.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos))); + POLYVOX_ASSERT(m_regValid.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)), "Position is outside valid region"); return m_pVolume.getVoxelAt(uXPos, uYPos, uZPos); } VoxelType getVoxelAt(const Vector3DInt32& v3dPos) const { - assert(m_regValid.containsPoint(v3dPos)); + POLYVOX_ASSERT(m_regValid.containsPoint(v3dPos), "Position is outside valid region"); return getVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ()); } void setVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue) const { - assert(m_regValid.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos))); + POLYVOX_ASSERT(m_regValid.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)), "Position is outside valid region"); m_pVolume.setVoxelAtConst(uXPos, uYPos, uZPos, tValue); } void setVoxelAt(const Vector3DInt32& v3dPos, VoxelType tValue) const { - assert(m_regValid.containsPoint(v3dPos)); + POLYVOX_ASSERT(m_regValid.containsPoint(v3dPos), "Position is outside valid region"); setVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tValue); } private: diff --git a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl index c9b793f5..5cf72fc6 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl @@ -224,7 +224,7 @@ namespace PolyVox // If we exit the loop here then apparently all the slots were full but none of them matched. I don't think // this can happen so let's put an assert to make sure. If you hit this assert then please report it to us! - assert(false); + POLYVOX_ASSERT(false, "All slots full but no matches."); return -1; //Should never happen. } diff --git a/library/PolyVoxCore/include/PolyVoxCore/Interpolation.h b/library/PolyVoxCore/include/PolyVoxCore/Interpolation.h index 09c063c9..b0574dd8 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Interpolation.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Interpolation.h @@ -33,7 +33,7 @@ namespace PolyVox const Type& v0,const Type& v1, const float x) { - assert((x >= 0.0f) && (x <= 1.0f)); + POLYVOX_ASSERT((x >= 0.0f) && (x <= 1.0f), "Interpolation input out of 0.0 to 1.0 range."); //Interpolate along X Type v0_1 = (v1 - v0) * x + v0; @@ -46,8 +46,8 @@ namespace PolyVox const Type& v00,const Type& v10,const Type& v01,const Type& v11, const float x, const float y) { - assert((x >= 0.0f) && (y >= 0.0f) && - (x <= 1.0f) && (y <= 1.0f)); + POLYVOX_ASSERT((x >= 0.0f) && (y >= 0.0f) && + (x <= 1.0f) && (y <= 1.0f), "Interpolation input out of 0.0 to 1.0 range."); // Linearly interpolate along x Type v00_10 = lerp(v00, v10, x); @@ -65,8 +65,8 @@ namespace PolyVox const Type& v001,const Type& v101,const Type& v011,const Type& v111, const float x, const float y, const float z) { - assert((x >= 0.0f) && (y >= 0.0f) && (z >= 0.0f) && - (x <= 1.0f) && (y <= 1.0f) && (z <= 1.0f)); + POLYVOX_ASSERT((x >= 0.0f) && (y >= 0.0f) && (z >= 0.0f) && + (x <= 1.0f) && (y <= 1.0f) && (z <= 1.0f), "Interpolation input out of 0.0 to 1.0 range."); // Bilinearly interpolate along Y Type v000_v100__v010_v110 = bilerp(v000, v100, v010, v110, x, y); diff --git a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl index fc476908..5cc06a9a 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl @@ -87,7 +87,7 @@ namespace PolyVox template LargeVolume::LargeVolume(const LargeVolume& /*rhs*/) { - assert(false); // See function comment above. + POLYVOX_ASSERT(false, "Copy constructor not implemented."); // See function comment above. } //////////////////////////////////////////////////////////////////////////////// @@ -109,7 +109,7 @@ namespace PolyVox template LargeVolume& LargeVolume::operator=(const LargeVolume& /*rhs*/) { - assert(false); // See function comment above. + POLYVOX_ASSERT(false, "Assignment operator not implemented."); // See function comment above. } //////////////////////////////////////////////////////////////////////////////// @@ -121,7 +121,7 @@ namespace PolyVox template VoxelType LargeVolume::getVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos) const { - assert(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos))); + POLYVOX_ASSERT(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)), "Position is outside valid region"); const int32_t blockX = uXPos >> m_uBlockSideLengthPower; const int32_t blockY = uYPos >> m_uBlockSideLengthPower; @@ -225,7 +225,7 @@ namespace PolyVox default: { //Should never happen - assert(false); + POLYVOX_ASSERT(false, "Invlaid case."); return VoxelType(0); } } @@ -303,7 +303,7 @@ namespace PolyVox template bool LargeVolume::setVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue) { - assert(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos))); + POLYVOX_ASSERT(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)), "Position is outside valid region"); const int32_t blockX = uXPos >> m_uBlockSideLengthPower; const int32_t blockY = uYPos >> m_uBlockSideLengthPower; @@ -468,7 +468,8 @@ namespace PolyVox void LargeVolume::initialise(const Region& regValidRegion, uint16_t uBlockSideLength) { //Debug mode validation - assert(uBlockSideLength > 0); + POLYVOX_ASSERT(uBlockSideLength > 0, "Block side length cannot be zero."); + POLYVOX_ASSERT(isPowerOf2(uBlockSideLength), "Block side length must be a power of two."); //Release mode validation if(uBlockSideLength == 0) @@ -583,7 +584,7 @@ namespace PolyVox //This check should also provide a significant speed boost as usually it is true. if((v3dBlockPos == m_v3dLastAccessedBlockPos) && (m_pLastAccessedBlock != 0)) { - assert(m_pLastAccessedBlock->m_tUncompressedData); + POLYVOX_ASSERT(m_pLastAccessedBlock->m_tUncompressedData, "Block has no uncompressed data"); return m_pLastAccessedBlock; } @@ -643,7 +644,7 @@ namespace PolyVox if(loadedBlock.block.m_bIsCompressed == false) { - assert(m_pLastAccessedBlock->m_tUncompressedData); + POLYVOX_ASSERT(m_pLastAccessedBlock->m_tUncompressedData, "Block has no uncompressed data"); return m_pLastAccessedBlock; } @@ -680,7 +681,7 @@ namespace PolyVox loadedBlock.block.uncompress(); m_pLastAccessedBlock = &(loadedBlock.block); - assert(m_pLastAccessedBlock->m_tUncompressedData); + POLYVOX_ASSERT(m_pLastAccessedBlock->m_tUncompressedData, "Block has no uncompressed data"); return m_pLastAccessedBlock; } diff --git a/library/PolyVoxCore/include/PolyVoxCore/LargeVolumeSampler.inl b/library/PolyVoxCore/include/PolyVoxCore/LargeVolumeSampler.inl index b6f3fc4f..024b3cdb 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/LargeVolumeSampler.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/LargeVolumeSampler.inl @@ -143,7 +143,7 @@ namespace PolyVox }*/ //Need to think what effect this has on any existing iterators. - assert(false); + POLYVOX_ASSERT(false, "This function cnnot be used on LargeVolume samplers."); return false; } From 4ee55bba2ec243ae0e23a48298eca9fe342f02ce Mon Sep 17 00:00:00 2001 From: David Williams Date: Tue, 1 Jan 2013 15:34:34 +0000 Subject: [PATCH 03/38] More replacing assert() with POLYVOX_ASSERT --- .../include/PolyVoxCore/LowPassFilter.inl | 4 ++-- .../MarchingCubesSurfaceExtractor.inl | 12 ---------- .../include/PolyVoxCore/RawVolume.inl | 14 ++++++------ .../include/PolyVoxCore/SimpleVolume.inl | 22 +++++++++---------- .../include/PolyVoxCore/SimpleVolumeBlock.inl | 18 +++++++-------- .../include/PolyVoxCore/SurfaceMesh.inl | 14 ++++++------ library/PolyVoxCore/source/Region.cpp | 2 +- 7 files changed, 37 insertions(+), 49 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/LowPassFilter.inl b/library/PolyVoxCore/include/PolyVoxCore/LowPassFilter.inl index a3cb81b1..cb455921 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/LowPassFilter.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/LowPassFilter.inl @@ -39,11 +39,11 @@ namespace PolyVox ,m_uKernelSize(uKernelSize) { //Kernel size must be at least three - assert(m_uKernelSize >= 3); + POLYVOX_ASSERT(m_uKernelSize >= 3, "Kernel size must be at least three"); m_uKernelSize = std::max(m_uKernelSize, static_cast(3)); //For release builds //Kernel size must be odd - assert(m_uKernelSize % 2 == 1); + POLYVOX_ASSERT(m_uKernelSize % 2 == 1, "Kernel size must be odd"); if(m_uKernelSize % 2 == 0) //For release builds { m_uKernelSize++; diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl index 1ca352eb..78e90c0a 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl @@ -550,62 +550,50 @@ namespace PolyVox if (edgeTable[iCubeIndex] & 1) { indlist[0] = m_pPreviousVertexIndicesX[uXRegSpace][uYRegSpace]; - //assert(indlist[0] != -1); } if (edgeTable[iCubeIndex] & 2) { indlist[1] = m_pPreviousVertexIndicesY[uXRegSpace+1][uYRegSpace]; - //assert(indlist[1] != -1); } if (edgeTable[iCubeIndex] & 4) { indlist[2] = m_pPreviousVertexIndicesX[uXRegSpace][uYRegSpace+1]; - //assert(indlist[2] != -1); } if (edgeTable[iCubeIndex] & 8) { indlist[3] = m_pPreviousVertexIndicesY[uXRegSpace][uYRegSpace]; - //assert(indlist[3] != -1); } if (edgeTable[iCubeIndex] & 16) { indlist[4] = m_pCurrentVertexIndicesX[uXRegSpace][uYRegSpace]; - //assert(indlist[4] != -1); } if (edgeTable[iCubeIndex] & 32) { indlist[5] = m_pCurrentVertexIndicesY[uXRegSpace+1][uYRegSpace]; - //assert(indlist[5] != -1); } if (edgeTable[iCubeIndex] & 64) { indlist[6] = m_pCurrentVertexIndicesX[uXRegSpace][uYRegSpace+1]; - //assert(indlist[6] != -1); } if (edgeTable[iCubeIndex] & 128) { indlist[7] = m_pCurrentVertexIndicesY[uXRegSpace][uYRegSpace]; - //assert(indlist[7] != -1); } if (edgeTable[iCubeIndex] & 256) { indlist[8] = m_pPreviousVertexIndicesZ[uXRegSpace][uYRegSpace]; - //assert(indlist[8] != -1); } if (edgeTable[iCubeIndex] & 512) { indlist[9] = m_pPreviousVertexIndicesZ[uXRegSpace+1][uYRegSpace]; - //assert(indlist[9] != -1); } if (edgeTable[iCubeIndex] & 1024) { indlist[10] = m_pPreviousVertexIndicesZ[uXRegSpace+1][uYRegSpace+1]; - //assert(indlist[10] != -1); } if (edgeTable[iCubeIndex] & 2048) { indlist[11] = m_pPreviousVertexIndicesZ[uXRegSpace][uYRegSpace+1]; - //assert(indlist[11] != -1); } for (int i=0;triTable[iCubeIndex][i]!=-1;i+=3) diff --git a/library/PolyVoxCore/include/PolyVoxCore/RawVolume.inl b/library/PolyVoxCore/include/PolyVoxCore/RawVolume.inl index 8a0a2e34..17e300e6 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/RawVolume.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/RawVolume.inl @@ -47,7 +47,7 @@ namespace PolyVox template RawVolume::RawVolume(const RawVolume& /*rhs*/) { - assert(false); // See function comment above. + POLYVOX_ASSERT(false, "Copy constructor not implemented."); // See function comment above. } //////////////////////////////////////////////////////////////////////////////// @@ -70,7 +70,7 @@ namespace PolyVox template RawVolume& RawVolume::operator=(const RawVolume& /*rhs*/) { - assert(false); // See function comment above. + POLYVOX_ASSERT(false, "Assignment operator not implemented."); // See function comment above. } //////////////////////////////////////////////////////////////////////////////// @@ -82,7 +82,7 @@ namespace PolyVox template VoxelType RawVolume::getVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos) const { - assert(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos))); + POLYVOX_ASSERT(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)), "Position is outside valid region"); const Vector3DInt32& v3dLowerCorner = this->m_regValidRegion.getLowerCorner(); int32_t iLocalXPos = uXPos - v3dLowerCorner.getX(); @@ -186,7 +186,7 @@ namespace PolyVox default: { //Should never happen - assert(false); + POLYVOX_ASSERT(false, "Invalid case."); return VoxelType(0); } } @@ -255,9 +255,9 @@ namespace PolyVox this->m_regValidRegion = regValidRegion; //Ensure dimensions of the specified Region are valid - assert(this->getWidth() > 0); - assert(this->getHeight() > 0); - assert(this->getDepth() > 0); + POLYVOX_ASSERT(this->getWidth() > 0, "Volume width must be greater than zero."); + POLYVOX_ASSERT(this->getHeight() > 0, "Volume width must be greater than zero."); + POLYVOX_ASSERT(this->getDepth() > 0, "Volume width must be greater than zero."); //Create the data m_pData = new VoxelType[this->getWidth() * this->getHeight()* this->getDepth()]; diff --git a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.inl b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.inl index a818d70b..938cd997 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.inl @@ -48,7 +48,7 @@ namespace PolyVox template SimpleVolume::SimpleVolume(const SimpleVolume& /*rhs*/) { - assert(false); // See function comment above. + POLYVOX_ASSERT(false, "Copy constructor not implemented."); // See function comment above. } //////////////////////////////////////////////////////////////////////////////// @@ -70,7 +70,7 @@ namespace PolyVox template SimpleVolume& SimpleVolume::operator=(const SimpleVolume& /*rhs*/) { - assert(false); // See function comment above. + POLYVOX_ASSERT(false, "Assignment operator not implemented."); // See function comment above. } //////////////////////////////////////////////////////////////////////////////// @@ -82,7 +82,7 @@ namespace PolyVox template VoxelType SimpleVolume::getVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos) const { - assert(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos))); + POLYVOX_ASSERT(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)), "Position is outside valid region"); const int32_t blockX = uXPos >> m_uBlockSideLengthPower; const int32_t blockY = uYPos >> m_uBlockSideLengthPower; @@ -186,7 +186,7 @@ namespace PolyVox default: { //Should never happen - assert(false); + POLYVOX_ASSERT(false, "Invalid case."); return VoxelType(0); } } @@ -212,7 +212,7 @@ namespace PolyVox template bool SimpleVolume::setVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue) { - assert(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos))); + POLYVOX_ASSERT(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)), "Position is outside valid region"); const int32_t blockX = uXPos >> m_uBlockSideLengthPower; const int32_t blockY = uYPos >> m_uBlockSideLengthPower; @@ -248,9 +248,9 @@ namespace PolyVox void SimpleVolume::initialise(const Region& regValidRegion, uint16_t uBlockSideLength) { //Debug mode validation - assert(uBlockSideLength >= 8); - assert(uBlockSideLength <= 256); - assert(isPowerOf2(uBlockSideLength)); + POLYVOX_ASSERT(uBlockSideLength >= 8, "Block side length should be at least 8"); + POLYVOX_ASSERT(uBlockSideLength <= 256, "Block side length should not be more than 256"); + POLYVOX_ASSERT(isPowerOf2(uBlockSideLength), "Block side length must be a power of two."); //Release mode validation if(uBlockSideLength < 8) @@ -308,9 +308,9 @@ namespace PolyVox uBlockY -= m_regValidRegionInBlocks.getLowerCorner().getY(); uBlockZ -= m_regValidRegionInBlocks.getLowerCorner().getZ(); - assert(uBlockX >= 0); - assert(uBlockY >= 0); - assert(uBlockZ >= 0); + POLYVOX_ASSERT(uBlockX >= 0, "Block coordinate must not be negative."); + POLYVOX_ASSERT(uBlockY >= 0, "Block coordinate must not be negative."); + POLYVOX_ASSERT(uBlockZ >= 0, "Block coordinate must not be negative."); //Compute the block index uint32_t uBlockIndex = diff --git a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolumeBlock.inl b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolumeBlock.inl index e603ff81..c65ca4cb 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolumeBlock.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolumeBlock.inl @@ -52,11 +52,11 @@ namespace PolyVox template VoxelType SimpleVolume::Block::getVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const { - assert(uXPos < m_uSideLength); - assert(uYPos < m_uSideLength); - assert(uZPos < m_uSideLength); + POLYVOX_ASSERT(uXPos < m_uSideLength, "Position is outside of the block."); + POLYVOX_ASSERT(uYPos < m_uSideLength, "Position is outside of the block."); + POLYVOX_ASSERT(uZPos < m_uSideLength, "Position is outside of the block."); - assert(m_tUncompressedData); + POLYVOX_ASSERT(m_tUncompressedData, "No uncompressed data available"); return m_tUncompressedData [ @@ -75,11 +75,11 @@ namespace PolyVox template void SimpleVolume::Block::setVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos, VoxelType tValue) { - assert(uXPos < m_uSideLength); - assert(uYPos < m_uSideLength); - assert(uZPos < m_uSideLength); + POLYVOX_ASSERT(uXPos < m_uSideLength, "Position is outside of the block."); + POLYVOX_ASSERT(uYPos < m_uSideLength, "Position is outside of the block."); + POLYVOX_ASSERT(uZPos < m_uSideLength, "Position is outside of the block."); - assert(m_tUncompressedData); + POLYVOX_ASSERT(m_tUncompressedData, "No uncompressed data available"); m_tUncompressedData [ @@ -106,7 +106,7 @@ namespace PolyVox void SimpleVolume::Block::initialise(uint16_t uSideLength) { //Debug mode validation - assert(isPowerOf2(uSideLength)); + POLYVOX_ASSERT(isPowerOf2(uSideLength), "Block side length must be a power of two."); //Release mode validation if(!isPowerOf2(uSideLength)) diff --git a/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.inl b/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.inl index 1b9f2bd0..1f48a9e2 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.inl @@ -103,9 +103,9 @@ namespace PolyVox void SurfaceMesh::addTriangle(uint32_t index0, uint32_t index1, uint32_t index2) { //Make sure the specified indices correspond to valid vertices. - assert(index0 < m_vecVertices.size()); - assert(index1 < m_vecVertices.size()); - assert(index2 < m_vecVertices.size()); + POLYVOX_ASSERT(index0 < m_vecVertices.size(), "Index points at an invalid vertex."); + POLYVOX_ASSERT(index1 < m_vecVertices.size(), "Index points at an invalid vertex."); + POLYVOX_ASSERT(index2 < m_vecVertices.size(), "Index points at an invalid vertex."); m_vecTriangleIndices.push_back(index0); m_vecTriangleIndices.push_back(index1); @@ -116,9 +116,9 @@ namespace PolyVox void SurfaceMesh::addTriangleCubic(uint32_t index0, uint32_t index1, uint32_t index2) { //Make sure the specified indices correspond to valid vertices. - assert(index0 < m_vecVertices.size()); - assert(index1 < m_vecVertices.size()); - assert(index2 < m_vecVertices.size()); + POLYVOX_ASSERT(index0 < m_vecVertices.size(), "Index points at an invalid vertex."); + POLYVOX_ASSERT(index1 < m_vecVertices.size(), "Index points at an invalid vertex."); + POLYVOX_ASSERT(index2 < m_vecVertices.size(), "Index points at an invalid vertex."); m_vecTriangleIndices.push_back(index0); m_vecTriangleIndices.push_back(index1); @@ -408,7 +408,7 @@ namespace PolyVox return result; } - assert(inputMesh.m_vecLodRecords.size() == 1); + POLYVOX_ASSERT(inputMesh.m_vecLodRecords.size() == 1, "Number of LOD records must equal one."); if(inputMesh.m_vecLodRecords.size() != 1) { //If we have done progressive LOD then it's too late to split into subsets. diff --git a/library/PolyVoxCore/source/Region.cpp b/library/PolyVoxCore/source/Region.cpp index 143a537a..6d896d7a 100644 --- a/library/PolyVoxCore/source/Region.cpp +++ b/library/PolyVoxCore/source/Region.cpp @@ -79,7 +79,7 @@ namespace PolyVox */ void Region::accumulate(const Region& reg) { - assert(reg.isValid()); //The result of accumulating an invalid region is not defined. + POLYVOX_ASSERT(reg.isValid(), "You cannot accumulate an invalid region."); //The result of accumulating an invalid region is not defined. m_iLowerX = ((std::min)(m_iLowerX, reg.getLowerX())); m_iLowerY = ((std::min)(m_iLowerY, reg.getLowerY())); From ff789d296df5a7d6511e2be14b2702c2552834a2 Mon Sep 17 00:00:00 2001 From: David Williams Date: Tue, 1 Jan 2013 18:08:59 +0000 Subject: [PATCH 04/38] Removed remaining traces of assert() --- library/PolyVoxCore/include/PolyVoxCore/BaseVolume.h | 1 - library/PolyVoxCore/include/PolyVoxCore/Density.h | 1 - library/PolyVoxCore/include/PolyVoxCore/Impl/Utility.h | 2 -- library/PolyVoxCore/include/PolyVoxCore/Interpolation.h | 2 -- library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h | 1 - library/PolyVoxCore/include/PolyVoxCore/Material.h | 2 -- library/PolyVoxCore/include/PolyVoxCore/RawVolume.h | 1 - library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.h | 1 - library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.inl | 2 -- library/PolyVoxCore/source/Region.cpp | 1 - 10 files changed, 14 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.h b/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.h index 5d56ac03..a7013746 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.h +++ b/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.h @@ -28,7 +28,6 @@ freely, subject to the following restrictions: #include "PolyVoxCore/Region.h" #include "PolyVoxCore/Vector.h" -#include #include namespace PolyVox diff --git a/library/PolyVoxCore/include/PolyVoxCore/Density.h b/library/PolyVoxCore/include/PolyVoxCore/Density.h index 4d60ad53..f44d8875 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Density.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Density.h @@ -28,7 +28,6 @@ freely, subject to the following restrictions: #include "Impl/TypeDef.h" -#include #include #undef min diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Utility.h b/library/PolyVoxCore/include/PolyVoxCore/Impl/Utility.h index 77dac7a4..b46df744 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Utility.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Utility.h @@ -26,8 +26,6 @@ freely, subject to the following restrictions: #include "PolyVoxCore/Impl/TypeDef.h" -#include - namespace PolyVox { POLYVOX_API uint8_t logBase2(uint32_t uInput); diff --git a/library/PolyVoxCore/include/PolyVoxCore/Interpolation.h b/library/PolyVoxCore/include/PolyVoxCore/Interpolation.h index b0574dd8..4b6f2539 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Interpolation.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Interpolation.h @@ -24,8 +24,6 @@ freely, subject to the following restrictions: #ifndef __PolyVox_Interpolation_H__ #define __PolyVox_Interpolation_H__ -#include - namespace PolyVox { template diff --git a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h index e21f6f16..7347a91f 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h +++ b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h @@ -31,7 +31,6 @@ freely, subject to the following restrictions: #include "PolyVoxCore/Vector.h" #include -#include #include //For abort() #include //For memcpy #include diff --git a/library/PolyVoxCore/include/PolyVoxCore/Material.h b/library/PolyVoxCore/include/PolyVoxCore/Material.h index be056afd..b8b80b2e 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Material.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Material.h @@ -28,8 +28,6 @@ freely, subject to the following restrictions: #include "PolyVoxCore/DefaultIsQuadNeeded.h" //we'll specialise this function for this voxel type -#include - namespace PolyVox { ///This class represents a voxel storing only a material. diff --git a/library/PolyVoxCore/include/PolyVoxCore/RawVolume.h b/library/PolyVoxCore/include/PolyVoxCore/RawVolume.h index f0ef1c6e..07ae1b37 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/RawVolume.h +++ b/library/PolyVoxCore/include/PolyVoxCore/RawVolume.h @@ -29,7 +29,6 @@ freely, subject to the following restrictions: #include "PolyVoxCore/Region.h" #include "PolyVoxCore/Vector.h" -#include #include //For abort() #include #include diff --git a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.h b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.h index 95326ee7..896d5480 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.h +++ b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.h @@ -31,7 +31,6 @@ freely, subject to the following restrictions: #include "PolyVoxCore/Region.h" #include "PolyVoxCore/Vector.h" -#include #include //For abort() #include //For memcpy #include diff --git a/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.inl b/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.inl index 1f48a9e2..0ce70aea 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.inl @@ -21,8 +21,6 @@ freely, subject to the following restrictions: distribution. *******************************************************************************/ -#include - namespace PolyVox { template diff --git a/library/PolyVoxCore/source/Region.cpp b/library/PolyVoxCore/source/Region.cpp index 6d896d7a..cfd54869 100644 --- a/library/PolyVoxCore/source/Region.cpp +++ b/library/PolyVoxCore/source/Region.cpp @@ -23,7 +23,6 @@ freely, subject to the following restrictions: #include "PolyVoxCore/Region.h" -#include #include namespace PolyVox From c05293844f4466c7e7bf2cbc03a82a866b17b689 Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Wed, 2 Jan 2013 14:13:14 +0100 Subject: [PATCH 05/38] Work on volume tests. --- tests/testvolume.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/testvolume.cpp b/tests/testvolume.cpp index 47130f95..d5b424f7 100644 --- a/tests/testvolume.cpp +++ b/tests/testvolume.cpp @@ -61,15 +61,15 @@ VolumeType* createAndFillVolume(void) } template -int32_t testDirectAccessWithWrapping(const VolumeType* volume) +int32_t testDirectAccessWithWrapping(const VolumeType* volume, int lowXOffset, int lowYOffset, int lowZOffset, int highXOffset, int highYOffset, int highZOffset) { int32_t result = 0; - for(int z = volume->getEnclosingRegion().getLowerZ() - 2; z <= volume->getEnclosingRegion().getUpperZ() + 4; z++) + for(int z = volume->getEnclosingRegion().getLowerZ() + lowXOffset; z <= volume->getEnclosingRegion().getUpperZ() + highXOffset; z++) { - for(int y = volume->getEnclosingRegion().getLowerY() - 3; y <= volume->getEnclosingRegion().getUpperY() + 5; y++) + for(int y = volume->getEnclosingRegion().getLowerY() + lowYOffset; y <= volume->getEnclosingRegion().getUpperY() + highYOffset; y++) { - for(int x = volume->getEnclosingRegion().getLowerX() - 1; x <= volume->getEnclosingRegion().getUpperX() + 2; x++) + for(int x = volume->getEnclosingRegion().getLowerX() + lowZOffset; x <= volume->getEnclosingRegion().getUpperX() + highZOffset; x++) { //Three level loop now processes 27 voxel neighbourhood for(int innerZ = -1; innerZ <=1; innerZ++) @@ -91,18 +91,18 @@ int32_t testDirectAccessWithWrapping(const VolumeType* volume) } template -int32_t testSamplersWithWrapping(VolumeType* volume) +int32_t testSamplersWithWrapping(VolumeType* volume, int lowXOffset, int lowYOffset, int lowZOffset, int highXOffset, int highYOffset, int highZOffset) { int32_t result = 0; typename VolumeType::Sampler sampler(volume); sampler.setWrapMode(WrapModes::Border, 3); - for(int z = volume->getEnclosingRegion().getLowerZ() - 2; z <= volume->getEnclosingRegion().getUpperZ() + 4; z++) + for(int z = volume->getEnclosingRegion().getLowerZ() + lowXOffset; z <= volume->getEnclosingRegion().getUpperZ() + highXOffset; z++) { - for(int y = volume->getEnclosingRegion().getLowerY() - 3; y <= volume->getEnclosingRegion().getUpperY() + 5; y++) + for(int y = volume->getEnclosingRegion().getLowerY() + lowYOffset; y <= volume->getEnclosingRegion().getUpperY() + highYOffset; y++) { - for(int x = volume->getEnclosingRegion().getLowerX() - 1; x <= volume->getEnclosingRegion().getUpperX() + 2; x++) + for(int x = volume->getEnclosingRegion().getLowerX() + lowZOffset; x <= volume->getEnclosingRegion().getUpperX() + highZOffset; x++) { sampler.setPosition(x, y, z); @@ -293,7 +293,7 @@ void TestVolume::testRawVolumeDirectAccess() QBENCHMARK { - result = testDirectAccessWithWrapping(m_pRawVolume); + result = testDirectAccessWithWrapping(m_pRawVolume, -2, -3, -1, 4, 5, 2); } QCOMPARE(result, static_cast(-928601007)); } @@ -304,7 +304,7 @@ void TestVolume::testRawVolumeSamplers() QBENCHMARK { - result = testSamplersWithWrapping(m_pRawVolume); + result = testSamplersWithWrapping(m_pRawVolume, -2, -3, -1, 4, 5, 2); } QCOMPARE(result, static_cast(-928601007)); } @@ -314,7 +314,7 @@ void TestVolume::testSimpleVolumeDirectAccess() int32_t result = 0; QBENCHMARK { - result = testDirectAccessWithWrapping(m_pSimpleVolume); + result = testDirectAccessWithWrapping(m_pSimpleVolume, -2, -3, -1, 4, 5, 2); } QCOMPARE(result, static_cast(-928601007)); } @@ -324,7 +324,7 @@ void TestVolume::testSimpleVolumeSamplers() int32_t result = 0; QBENCHMARK { - result = testSamplersWithWrapping(m_pSimpleVolume); + result = testSamplersWithWrapping(m_pSimpleVolume, -2, -3, -1, 4, 5, 2); } QCOMPARE(result, static_cast(-928601007)); } @@ -334,7 +334,7 @@ void TestVolume::testLargeVolumeDirectAccess() int32_t result = 0; QBENCHMARK { - result = testDirectAccessWithWrapping(m_pLargeVolume); + result = testDirectAccessWithWrapping(m_pLargeVolume, -2, -3, -1, 4, 5, 2); } QCOMPARE(result, static_cast(-928601007)); } @@ -345,7 +345,7 @@ void TestVolume::testLargeVolumeSamplers() QBENCHMARK { - result = testSamplersWithWrapping(m_pLargeVolume); + result = testSamplersWithWrapping(m_pLargeVolume, -2, -3, -1, 4, 5, 2); } QCOMPARE(result, static_cast(-928601007)); } From 1e9bb8833706d247c91d65cea900fbd0d9903655 Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Wed, 2 Jan 2013 14:13:42 +0100 Subject: [PATCH 06/38] Fixed compile errors in VS2008. --- examples/OpenGL/OpenGLWidget.h | 2 +- library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/OpenGL/OpenGLWidget.h b/examples/OpenGL/OpenGLWidget.h index f61a43ad..ef6ff636 100644 --- a/examples/OpenGL/OpenGLWidget.h +++ b/examples/OpenGL/OpenGLWidget.h @@ -41,7 +41,7 @@ const int32_t g_uVolumeSideLength = 128; struct Vector3DUint8Compare { - bool operator() (const PolyVox::Vector3DUint8& a, const PolyVox::Vector3DUint8& b) + bool operator() (const PolyVox::Vector3DUint8& a, const PolyVox::Vector3DUint8& b) const { const uint32_t size = 3; for(uint32_t ct = 0; ct < size; ++ct) diff --git a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h index 7347a91f..b35f0cae 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h +++ b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h @@ -312,7 +312,7 @@ namespace PolyVox struct BlockPositionCompare { - bool operator() (const PolyVox::Vector3DInt32& a, const PolyVox::Vector3DInt32& b) + bool operator() (const PolyVox::Vector3DInt32& a, const PolyVox::Vector3DInt32& b) const { const uint32_t size = 3; for(uint32_t ct = 0; ct < size; ++ct) From 97024ba9b29faacf597e123c0df2b8551de5d4f1 Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Wed, 2 Jan 2013 14:48:30 +0100 Subject: [PATCH 07/38] More volume test configurations. --- tests/testvolume.cpp | 103 ++++++++++++++++++++++++++++++++++++++++--- tests/testvolume.h | 18 +++++--- 2 files changed, 108 insertions(+), 13 deletions(-) diff --git a/tests/testvolume.cpp b/tests/testvolume.cpp index d5b424f7..95b60d4f 100644 --- a/tests/testvolume.cpp +++ b/tests/testvolume.cpp @@ -60,11 +60,16 @@ VolumeType* createAndFillVolume(void) return volume; } +// We allow user provided offset in this function so we can test the case when all samples are inside a volume and also the case when some samples are outside. +// This is important because samplers are often slower when outside the volume as they have to fall back on directly accessing the volume data. template int32_t testDirectAccessWithWrapping(const VolumeType* volume, int lowXOffset, int lowYOffset, int lowZOffset, int highXOffset, int highYOffset, int highZOffset) { int32_t result = 0; + // If we know that we are only iterating over voxels internal to the volume then we can avoid calling the 'wrapping' function. This should be faster. + bool bAllVoxelsInternal = (lowXOffset > 0) && (lowYOffset > 0) && (lowZOffset > 0) && (highXOffset < 0) && (highYOffset < 0) && (highZOffset < 0); + for(int z = volume->getEnclosingRegion().getLowerZ() + lowXOffset; z <= volume->getEnclosingRegion().getUpperZ() + highXOffset; z++) { for(int y = volume->getEnclosingRegion().getLowerY() + lowYOffset; y <= volume->getEnclosingRegion().getUpperY() + highYOffset; y++) @@ -78,7 +83,16 @@ int32_t testDirectAccessWithWrapping(const VolumeType* volume, int lowXOffset, i { for(int innerX = -1; innerX <=1; innerX++) { - result = cantorTupleFunction(result, volume->getVoxelWithWrapping(x + innerX, y + innerY, z + innerZ, WrapModes::Border, 3)); + // Deeply nested 'if', but this is just a unit test and we should still + // see some performance improvement by skipping the wrapping versions. + if(bAllVoxelsInternal) + { + result = cantorTupleFunction(result, volume->getVoxel(x + innerX, y + innerY, z + innerZ)); + } + else + { + result = cantorTupleFunction(result, volume->getVoxelWithWrapping(x + innerX, y + innerY, z + innerZ, WrapModes::Border, 3)); + } } } } @@ -287,7 +301,33 @@ TestVolume::~TestVolume() delete m_pLargeVolume; } -void TestVolume::testRawVolumeDirectAccess() +/* + * RawVolume Tests + */ + +void TestVolume::testRawVolumeDirectAccessAllInternal() +{ + int32_t result = 0; + + QBENCHMARK + { + result = testDirectAccessWithWrapping(m_pRawVolume, 2, 2, 4, -2, -1, -3); + } + QCOMPARE(result, static_cast(1004598054)); +} + +void TestVolume::testRawVolumeSamplersAllInternal() +{ + int32_t result = 0; + + QBENCHMARK + { + result = testSamplersWithWrapping(m_pRawVolume, 2, 2, 4, -2, -1, -3); + } + QCOMPARE(result, static_cast(1004598054)); +} + +void TestVolume::testRawVolumeDirectAccessWithExternal() { int32_t result = 0; @@ -298,7 +338,7 @@ void TestVolume::testRawVolumeDirectAccess() QCOMPARE(result, static_cast(-928601007)); } -void TestVolume::testRawVolumeSamplers() +void TestVolume::testRawVolumeSamplersWithExternal() { int32_t result = 0; @@ -309,7 +349,31 @@ void TestVolume::testRawVolumeSamplers() QCOMPARE(result, static_cast(-928601007)); } -void TestVolume::testSimpleVolumeDirectAccess() +/* + * SimpleVolume Tests + */ + +void TestVolume::testSimpleVolumeDirectAccessAllInternal() +{ + int32_t result = 0; + QBENCHMARK + { + result = testDirectAccessWithWrapping(m_pSimpleVolume, 2, 2, 4, -2, -1, -3); + } + QCOMPARE(result, static_cast(1004598054)); +} + +void TestVolume::testSimpleVolumeSamplersAllInternal() +{ + int32_t result = 0; + QBENCHMARK + { + result = testSamplersWithWrapping(m_pSimpleVolume, 2, 2, 4, -2, -1, -3); + } + QCOMPARE(result, static_cast(1004598054)); +} + +void TestVolume::testSimpleVolumeDirectAccessWithExternal() { int32_t result = 0; QBENCHMARK @@ -319,7 +383,7 @@ void TestVolume::testSimpleVolumeDirectAccess() QCOMPARE(result, static_cast(-928601007)); } -void TestVolume::testSimpleVolumeSamplers() +void TestVolume::testSimpleVolumeSamplersWithExternal() { int32_t result = 0; QBENCHMARK @@ -329,7 +393,32 @@ void TestVolume::testSimpleVolumeSamplers() QCOMPARE(result, static_cast(-928601007)); } -void TestVolume::testLargeVolumeDirectAccess() +/* + * LargeVolume Tests + */ + +void TestVolume::testLargeVolumeDirectAccessAllInternal() +{ + int32_t result = 0; + QBENCHMARK + { + result = testDirectAccessWithWrapping(m_pLargeVolume, 2, 2, 4, -2, -1, -3); + } + QCOMPARE(result, static_cast(1004598054)); +} + +void TestVolume::testLargeVolumeSamplersAllInternal() +{ + int32_t result = 0; + + QBENCHMARK + { + result = testSamplersWithWrapping(m_pLargeVolume, 2, 2, 4, -2, -1, -3); + } + QCOMPARE(result, static_cast(1004598054)); +} + +void TestVolume::testLargeVolumeDirectAccessWithExternal() { int32_t result = 0; QBENCHMARK @@ -339,7 +428,7 @@ void TestVolume::testLargeVolumeDirectAccess() QCOMPARE(result, static_cast(-928601007)); } -void TestVolume::testLargeVolumeSamplers() +void TestVolume::testLargeVolumeSamplersWithExternal() { int32_t result = 0; diff --git a/tests/testvolume.h b/tests/testvolume.h index a714d90e..f970918b 100644 --- a/tests/testvolume.h +++ b/tests/testvolume.h @@ -37,14 +37,20 @@ public: ~TestVolume(); private slots: - void testRawVolumeDirectAccess(); - void testRawVolumeSamplers(); + void testRawVolumeDirectAccessAllInternal(); + void testRawVolumeSamplersAllInternal(); + void testRawVolumeDirectAccessWithExternal(); + void testRawVolumeSamplersWithExternal(); - void testSimpleVolumeDirectAccess(); - void testSimpleVolumeSamplers(); + void testSimpleVolumeDirectAccessAllInternal(); + void testSimpleVolumeSamplersAllInternal(); + void testSimpleVolumeDirectAccessWithExternal(); + void testSimpleVolumeSamplersWithExternal(); - void testLargeVolumeDirectAccess(); - void testLargeVolumeSamplers(); + void testLargeVolumeDirectAccessAllInternal(); + void testLargeVolumeSamplersAllInternal(); + void testLargeVolumeDirectAccessWithExternal(); + void testLargeVolumeSamplersWithExternal(); private: PolyVox::RawVolume* m_pRawVolume; From a4b6339689b9aa0abdf587658fc9635dc8cc7934 Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Wed, 2 Jan 2013 15:15:30 +0100 Subject: [PATCH 08/38] Fixed typos. --- tests/testvolume.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/testvolume.cpp b/tests/testvolume.cpp index 95b60d4f..ebdbcdaf 100644 --- a/tests/testvolume.cpp +++ b/tests/testvolume.cpp @@ -70,11 +70,11 @@ int32_t testDirectAccessWithWrapping(const VolumeType* volume, int lowXOffset, i // If we know that we are only iterating over voxels internal to the volume then we can avoid calling the 'wrapping' function. This should be faster. bool bAllVoxelsInternal = (lowXOffset > 0) && (lowYOffset > 0) && (lowZOffset > 0) && (highXOffset < 0) && (highYOffset < 0) && (highZOffset < 0); - for(int z = volume->getEnclosingRegion().getLowerZ() + lowXOffset; z <= volume->getEnclosingRegion().getUpperZ() + highXOffset; z++) + for(int z = volume->getEnclosingRegion().getLowerZ() + lowZOffset; z <= volume->getEnclosingRegion().getUpperZ() + highZOffset; z++) { for(int y = volume->getEnclosingRegion().getLowerY() + lowYOffset; y <= volume->getEnclosingRegion().getUpperY() + highYOffset; y++) { - for(int x = volume->getEnclosingRegion().getLowerX() + lowZOffset; x <= volume->getEnclosingRegion().getUpperX() + highZOffset; x++) + for(int x = volume->getEnclosingRegion().getLowerX() + lowXOffset; x <= volume->getEnclosingRegion().getUpperX() + highXOffset; x++) { //Three level loop now processes 27 voxel neighbourhood for(int innerZ = -1; innerZ <=1; innerZ++) @@ -112,11 +112,11 @@ int32_t testSamplersWithWrapping(VolumeType* volume, int lowXOffset, int lowYOff typename VolumeType::Sampler sampler(volume); sampler.setWrapMode(WrapModes::Border, 3); - for(int z = volume->getEnclosingRegion().getLowerZ() + lowXOffset; z <= volume->getEnclosingRegion().getUpperZ() + highXOffset; z++) + for(int z = volume->getEnclosingRegion().getLowerZ() + lowZOffset; z <= volume->getEnclosingRegion().getUpperZ() + highZOffset; z++) { for(int y = volume->getEnclosingRegion().getLowerY() + lowYOffset; y <= volume->getEnclosingRegion().getUpperY() + highYOffset; y++) { - for(int x = volume->getEnclosingRegion().getLowerX() + lowZOffset; x <= volume->getEnclosingRegion().getUpperX() + highZOffset; x++) + for(int x = volume->getEnclosingRegion().getLowerX() + lowXOffset; x <= volume->getEnclosingRegion().getUpperX() + highXOffset; x++) { sampler.setPosition(x, y, z); @@ -311,7 +311,7 @@ void TestVolume::testRawVolumeDirectAccessAllInternal() QBENCHMARK { - result = testDirectAccessWithWrapping(m_pRawVolume, 2, 2, 4, -2, -1, -3); + result = testDirectAccessWithWrapping(m_pRawVolume, 4, 2, 2, -3, -1, -2); } QCOMPARE(result, static_cast(1004598054)); } @@ -322,7 +322,7 @@ void TestVolume::testRawVolumeSamplersAllInternal() QBENCHMARK { - result = testSamplersWithWrapping(m_pRawVolume, 2, 2, 4, -2, -1, -3); + result = testSamplersWithWrapping(m_pRawVolume, 4, 2, 2, -3, -1, -2); } QCOMPARE(result, static_cast(1004598054)); } @@ -333,7 +333,7 @@ void TestVolume::testRawVolumeDirectAccessWithExternal() QBENCHMARK { - result = testDirectAccessWithWrapping(m_pRawVolume, -2, -3, -1, 4, 5, 2); + result = testDirectAccessWithWrapping(m_pRawVolume, -1, -3, -2, 2, 5, 4); } QCOMPARE(result, static_cast(-928601007)); } @@ -344,7 +344,7 @@ void TestVolume::testRawVolumeSamplersWithExternal() QBENCHMARK { - result = testSamplersWithWrapping(m_pRawVolume, -2, -3, -1, 4, 5, 2); + result = testSamplersWithWrapping(m_pRawVolume, -1, -3, -2, 2, 5, 4); } QCOMPARE(result, static_cast(-928601007)); } @@ -358,7 +358,7 @@ void TestVolume::testSimpleVolumeDirectAccessAllInternal() int32_t result = 0; QBENCHMARK { - result = testDirectAccessWithWrapping(m_pSimpleVolume, 2, 2, 4, -2, -1, -3); + result = testDirectAccessWithWrapping(m_pSimpleVolume, 4, 2, 2, -3, -1, -2); } QCOMPARE(result, static_cast(1004598054)); } @@ -368,7 +368,7 @@ void TestVolume::testSimpleVolumeSamplersAllInternal() int32_t result = 0; QBENCHMARK { - result = testSamplersWithWrapping(m_pSimpleVolume, 2, 2, 4, -2, -1, -3); + result = testSamplersWithWrapping(m_pSimpleVolume, 4, 2, 2, -3, -1, -2); } QCOMPARE(result, static_cast(1004598054)); } @@ -378,7 +378,7 @@ void TestVolume::testSimpleVolumeDirectAccessWithExternal() int32_t result = 0; QBENCHMARK { - result = testDirectAccessWithWrapping(m_pSimpleVolume, -2, -3, -1, 4, 5, 2); + result = testDirectAccessWithWrapping(m_pSimpleVolume, -1, -3, -2, 2, 5, 4); } QCOMPARE(result, static_cast(-928601007)); } @@ -388,7 +388,7 @@ void TestVolume::testSimpleVolumeSamplersWithExternal() int32_t result = 0; QBENCHMARK { - result = testSamplersWithWrapping(m_pSimpleVolume, -2, -3, -1, 4, 5, 2); + result = testSamplersWithWrapping(m_pSimpleVolume, -1, -3, -2, 2, 5, 4); } QCOMPARE(result, static_cast(-928601007)); } @@ -402,7 +402,7 @@ void TestVolume::testLargeVolumeDirectAccessAllInternal() int32_t result = 0; QBENCHMARK { - result = testDirectAccessWithWrapping(m_pLargeVolume, 2, 2, 4, -2, -1, -3); + result = testDirectAccessWithWrapping(m_pLargeVolume, 4, 2, 2, -3, -1, -2); } QCOMPARE(result, static_cast(1004598054)); } @@ -413,7 +413,7 @@ void TestVolume::testLargeVolumeSamplersAllInternal() QBENCHMARK { - result = testSamplersWithWrapping(m_pLargeVolume, 2, 2, 4, -2, -1, -3); + result = testSamplersWithWrapping(m_pLargeVolume, 4, 2, 2, -3, -1, -2); } QCOMPARE(result, static_cast(1004598054)); } @@ -423,7 +423,7 @@ void TestVolume::testLargeVolumeDirectAccessWithExternal() int32_t result = 0; QBENCHMARK { - result = testDirectAccessWithWrapping(m_pLargeVolume, -2, -3, -1, 4, 5, 2); + result = testDirectAccessWithWrapping(m_pLargeVolume, -1, -3, -2, 2, 5, 4); } QCOMPARE(result, static_cast(-928601007)); } @@ -434,7 +434,7 @@ void TestVolume::testLargeVolumeSamplersWithExternal() QBENCHMARK { - result = testSamplersWithWrapping(m_pLargeVolume, -2, -3, -1, 4, 5, 2); + result = testSamplersWithWrapping(m_pLargeVolume, -1, -3, -2, 2, 5, 4); } QCOMPARE(result, static_cast(-928601007)); } From 7bbaa0d559580f367804ba9581bbe44861ed3fac Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Wed, 2 Jan 2013 16:09:30 +0100 Subject: [PATCH 09/38] Better testing of volume samplers. --- tests/testvolume.cpp | 75 ++++++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/tests/testvolume.cpp b/tests/testvolume.cpp index ebdbcdaf..ada3a8db 100644 --- a/tests/testvolume.cpp +++ b/tests/testvolume.cpp @@ -109,48 +109,59 @@ int32_t testSamplersWithWrapping(VolumeType* volume, int lowXOffset, int lowYOff { int32_t result = 0; - typename VolumeType::Sampler sampler(volume); - sampler.setWrapMode(WrapModes::Border, 3); + //Test the sampler move functions + typename VolumeType::Sampler xSampler(volume); + typename VolumeType::Sampler ySampler(volume); + typename VolumeType::Sampler zSampler(volume); + xSampler.setWrapMode(WrapModes::Border, 3); + ySampler.setWrapMode(WrapModes::Border, 3); + zSampler.setWrapMode(WrapModes::Border, 3); + + zSampler.setPosition(volume->getEnclosingRegion().getLowerX() + lowXOffset, volume->getEnclosingRegion().getLowerY() + lowYOffset, volume->getEnclosingRegion().getLowerZ() + lowZOffset); for(int z = volume->getEnclosingRegion().getLowerZ() + lowZOffset; z <= volume->getEnclosingRegion().getUpperZ() + highZOffset; z++) { + ySampler = zSampler; for(int y = volume->getEnclosingRegion().getLowerY() + lowYOffset; y <= volume->getEnclosingRegion().getUpperY() + highYOffset; y++) { + xSampler = ySampler; for(int x = volume->getEnclosingRegion().getLowerX() + lowXOffset; x <= volume->getEnclosingRegion().getUpperX() + highXOffset; x++) { - sampler.setPosition(x, y, z); + result = cantorTupleFunction(result, xSampler.peekVoxel1nx1ny1nz()); + result = cantorTupleFunction(result, xSampler.peekVoxel0px1ny1nz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1px1ny1nz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1nx0py1nz()); + result = cantorTupleFunction(result, xSampler.peekVoxel0px0py1nz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1px0py1nz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1nx1py1nz()); + result = cantorTupleFunction(result, xSampler.peekVoxel0px1py1nz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1px1py1nz()); - result = cantorTupleFunction(result, sampler.peekVoxel1nx1ny1nz()); - result = cantorTupleFunction(result, sampler.peekVoxel0px1ny1nz()); - result = cantorTupleFunction(result, sampler.peekVoxel1px1ny1nz()); - result = cantorTupleFunction(result, sampler.peekVoxel1nx0py1nz()); - result = cantorTupleFunction(result, sampler.peekVoxel0px0py1nz()); - result = cantorTupleFunction(result, sampler.peekVoxel1px0py1nz()); - result = cantorTupleFunction(result, sampler.peekVoxel1nx1py1nz()); - result = cantorTupleFunction(result, sampler.peekVoxel0px1py1nz()); - result = cantorTupleFunction(result, sampler.peekVoxel1px1py1nz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1nx1ny0pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel0px1ny0pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1px1ny0pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1nx0py0pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel0px0py0pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1px0py0pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1nx1py0pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel0px1py0pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1px1py0pz()); - result = cantorTupleFunction(result, sampler.peekVoxel1nx1ny0pz()); - result = cantorTupleFunction(result, sampler.peekVoxel0px1ny0pz()); - result = cantorTupleFunction(result, sampler.peekVoxel1px1ny0pz()); - result = cantorTupleFunction(result, sampler.peekVoxel1nx0py0pz()); - result = cantorTupleFunction(result, sampler.peekVoxel0px0py0pz()); - result = cantorTupleFunction(result, sampler.peekVoxel1px0py0pz()); - result = cantorTupleFunction(result, sampler.peekVoxel1nx1py0pz()); - result = cantorTupleFunction(result, sampler.peekVoxel0px1py0pz()); - result = cantorTupleFunction(result, sampler.peekVoxel1px1py0pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1nx1ny1pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel0px1ny1pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1px1ny1pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1nx0py1pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel0px0py1pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1px0py1pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1nx1py1pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel0px1py1pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1px1py1pz()); - result = cantorTupleFunction(result, sampler.peekVoxel1nx1ny1pz()); - result = cantorTupleFunction(result, sampler.peekVoxel0px1ny1pz()); - result = cantorTupleFunction(result, sampler.peekVoxel1px1ny1pz()); - result = cantorTupleFunction(result, sampler.peekVoxel1nx0py1pz()); - result = cantorTupleFunction(result, sampler.peekVoxel0px0py1pz()); - result = cantorTupleFunction(result, sampler.peekVoxel1px0py1pz()); - result = cantorTupleFunction(result, sampler.peekVoxel1nx1py1pz()); - result = cantorTupleFunction(result, sampler.peekVoxel0px1py1pz()); - result = cantorTupleFunction(result, sampler.peekVoxel1px1py1pz()); + xSampler.movePositiveX(); } + ySampler.movePositiveY(); } + zSampler.movePositiveZ(); } return result; @@ -278,6 +289,10 @@ TestVolume::TestVolume() m_pSimpleVolume = new SimpleVolume(region); m_pLargeVolume = new LargeVolume(region); + // LargeVolume currently fails a test if compression is enabled. It + // may be related to accessing the data through more than one sampler? + m_pLargeVolume->setCompressionEnabled(false); + //Fill the volume with some data for(int z = region.getLowerZ(); z <= region.getUpperZ(); z++) { From 25fae419f307520d9e0024d9c6de120ef0ff547e Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Wed, 2 Jan 2013 16:11:19 +0100 Subject: [PATCH 10/38] Removed some old testing code. --- tests/testvolume.cpp | 134 ------------------------------------------- 1 file changed, 134 deletions(-) diff --git a/tests/testvolume.cpp b/tests/testvolume.cpp index ada3a8db..3ed9c599 100644 --- a/tests/testvolume.cpp +++ b/tests/testvolume.cpp @@ -39,27 +39,6 @@ inline int32_t cantorTupleFunction(int32_t previousResult, int32_t value) return (( previousResult + value ) * ( previousResult + value + 1 ) + value ) / 2; } -template -VolumeType* createAndFillVolume(void) -{ - //Create the volume - VolumeType* volume = new VolumeType(Region(-57, -31, 12, 64, 96, 131)); // Deliberatly awkward size - - //Fill the volume with some data - for(int z = volume->getEnclosingRegion().getLowerZ(); z <= volume->getEnclosingRegion().getUpperZ(); z++) - { - for(int y = volume->getEnclosingRegion().getLowerY(); y <= volume->getEnclosingRegion().getUpperY(); y++) - { - for(int x = volume->getEnclosingRegion().getLowerX(); x <= volume->getEnclosingRegion().getUpperX(); x++) - { - volume->setVoxelAt(x, y, z, x + y + z); - } - } - } - - return volume; -} - // We allow user provided offset in this function so we can test the case when all samples are inside a volume and also the case when some samples are outside. // This is important because samplers are often slower when outside the volume as they have to fall back on directly accessing the volume data. template @@ -167,119 +146,6 @@ int32_t testSamplersWithWrapping(VolumeType* volume, int lowXOffset, int lowYOff return result; } -template -int32_t complexVolumeTest(void) -{ - VolumeType* testVolume = createAndFillVolume(); - - int32_t result = 0; - - //Test the getVoxel function - for(int z = testVolume->getEnclosingRegion().getLowerZ(); z <= testVolume->getEnclosingRegion().getUpperZ(); z++) - { - for(int y = testVolume->getEnclosingRegion().getLowerY(); y <= testVolume->getEnclosingRegion().getUpperY(); y++) - { - for(int x = testVolume->getEnclosingRegion().getLowerX(); x <= testVolume->getEnclosingRegion().getUpperX(); x++) - { - result = cantorTupleFunction(result, testVolume->getVoxel(x, y, z)); - } - } - } - - //Test border wrap mode - for(int z = testVolume->getEnclosingRegion().getLowerZ(); z <= testVolume->getEnclosingRegion().getUpperZ(); z++) - { - //Extending outside in y - for(int y = testVolume->getEnclosingRegion().getLowerY() - 3; y <= testVolume->getEnclosingRegion().getUpperY() + 5; y++) - { - for(int x = testVolume->getEnclosingRegion().getLowerX(); x <= testVolume->getEnclosingRegion().getUpperX(); x++) - { - result = cantorTupleFunction(result, testVolume->getVoxelWithWrapping(x, y, z, WrapModes::Border, 3)); - } - } - } - - //Test clamp wrap mode - for(int z = testVolume->getEnclosingRegion().getLowerZ(); z <= testVolume->getEnclosingRegion().getUpperZ(); z++) - { - for(int y = testVolume->getEnclosingRegion().getLowerY(); y <= testVolume->getEnclosingRegion().getUpperY(); y++) - { - //Extending outside in x - for(int x = testVolume->getEnclosingRegion().getLowerX() - 2; x <= testVolume->getEnclosingRegion().getUpperX() + 4; x++) - { - result = cantorTupleFunction(result, testVolume->getVoxelWithWrapping(x, y, z, WrapModes::Clamp)); - } - } - } - - //Test the sampler setPosition - typename VolumeType::Sampler sampler(testVolume); - sampler.setWrapMode(WrapModes::Border, 1); - - for(int z = testVolume->getEnclosingRegion().getLowerZ() - 2; z <= testVolume->getEnclosingRegion().getUpperZ() + 1; z++) - { - for(int y = testVolume->getEnclosingRegion().getLowerY() - 1; y <= testVolume->getEnclosingRegion().getUpperY() + 3; y++) - { - for(int x = testVolume->getEnclosingRegion().getLowerX() - 4; x <= testVolume->getEnclosingRegion().getUpperX() + 2; x++) - { - sampler.setPosition(x,y,z); - result = cantorTupleFunction(result, sampler.getVoxel()); - } - } - } - - //Test the sampler move functions - typename VolumeType::Sampler xSampler(testVolume); - typename VolumeType::Sampler ySampler(testVolume); - typename VolumeType::Sampler zSampler(testVolume); - - xSampler.setWrapMode(WrapModes::Border, 1); - ySampler.setWrapMode(WrapModes::Clamp, 1); - zSampler.setWrapMode(WrapModes::Border, -3); - - zSampler.setPosition(testVolume->getEnclosingRegion().getLowerX() - 4, testVolume->getEnclosingRegion().getLowerY() - 1, testVolume->getEnclosingRegion().getLowerZ() - 2); - for(int z = testVolume->getEnclosingRegion().getLowerZ() - 2; z <= testVolume->getEnclosingRegion().getUpperZ() + 1; z++) - { - ySampler = zSampler; - for(int y = testVolume->getEnclosingRegion().getLowerY() - 1; y <= testVolume->getEnclosingRegion().getUpperY() + 3; y++) - { - xSampler = ySampler; - for(int x = testVolume->getEnclosingRegion().getLowerX() - 4; x <= testVolume->getEnclosingRegion().getUpperX() + 2; x++) - { - result = cantorTupleFunction(result, xSampler.getVoxel()); - xSampler.movePositiveX(); - } - ySampler.movePositiveY(); - } - zSampler.movePositiveZ(); - } - - xSampler.setWrapMode(WrapModes::Clamp); - ySampler.setWrapMode(WrapModes::Border, 1); - zSampler.setWrapMode(WrapModes::Clamp, -1); - - zSampler.setPosition(testVolume->getEnclosingRegion().getUpperX() + 2, testVolume->getEnclosingRegion().getUpperY() + 3, testVolume->getEnclosingRegion().getUpperZ() + 1); - for(int z = 0; z < testVolume->getEnclosingRegion().getDepthInVoxels() + 8; z++) - { - ySampler = zSampler; - for(int y = 0; y < testVolume->getEnclosingRegion().getHeightInVoxels() + 3; y++) - { - xSampler = ySampler; - for(int x = 0; x < testVolume->getEnclosingRegion().getWidthInVoxels() + 5; x++) - { - result = cantorTupleFunction(result, xSampler.getVoxel()); - xSampler.moveNegativeX(); - } - ySampler.moveNegativeY(); - } - zSampler.moveNegativeZ(); - } - - delete testVolume; - - return result; -} - TestVolume::TestVolume() { Region region(-57, -31, 12, 64, 96, 131); // Deliberatly awkward size From 4b45bb297f2457166dfd6b4da3b30964c47f33e3 Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Wed, 2 Jan 2013 17:05:35 +0100 Subject: [PATCH 11/38] More volume unit tests - now iterating backwards as well as forwards. --- tests/testvolume.cpp | 293 +++++++++++++++++++++++++++++++++++++++---- tests/testvolume.h | 36 ++++-- 2 files changed, 291 insertions(+), 38 deletions(-) diff --git a/tests/testvolume.cpp b/tests/testvolume.cpp index 3ed9c599..e177d7bc 100644 --- a/tests/testvolume.cpp +++ b/tests/testvolume.cpp @@ -39,10 +39,14 @@ inline int32_t cantorTupleFunction(int32_t previousResult, int32_t value) return (( previousResult + value ) * ( previousResult + value + 1 ) + value ) / 2; } +/* + * Funtions for testing iteration in a forwards direction + */ + // We allow user provided offset in this function so we can test the case when all samples are inside a volume and also the case when some samples are outside. // This is important because samplers are often slower when outside the volume as they have to fall back on directly accessing the volume data. template -int32_t testDirectAccessWithWrapping(const VolumeType* volume, int lowXOffset, int lowYOffset, int lowZOffset, int highXOffset, int highYOffset, int highZOffset) +int32_t testDirectAccessWithWrappingForwards(const VolumeType* volume, int lowXOffset, int lowYOffset, int lowZOffset, int highXOffset, int highYOffset, int highZOffset) { int32_t result = 0; @@ -84,7 +88,7 @@ int32_t testDirectAccessWithWrapping(const VolumeType* volume, int lowXOffset, i } template -int32_t testSamplersWithWrapping(VolumeType* volume, int lowXOffset, int lowYOffset, int lowZOffset, int highXOffset, int highYOffset, int highZOffset) +int32_t testSamplersWithWrappingForwards(VolumeType* volume, int lowXOffset, int lowYOffset, int lowZOffset, int highXOffset, int highYOffset, int highZOffset) { int32_t result = 0; @@ -146,6 +150,117 @@ int32_t testSamplersWithWrapping(VolumeType* volume, int lowXOffset, int lowYOff return result; } +/* + * Funtions for testing iteration in a backwards direction + */ + +// We allow user provided offset in this function so we can test the case when all samples are inside a volume and also the case when some samples are outside. +// This is important because samplers are often slower when outside the volume as they have to fall back on directly accessing the volume data. +template +int32_t testDirectAccessWithWrappingBackwards(const VolumeType* volume, int lowXOffset, int lowYOffset, int lowZOffset, int highXOffset, int highYOffset, int highZOffset) +{ + int32_t result = 0; + + // If we know that we are only iterating over voxels internal to the volume then we can avoid calling the 'wrapping' function. This should be faster. + bool bAllVoxelsInternal = (lowXOffset > 0) && (lowYOffset > 0) && (lowZOffset > 0) && (highXOffset < 0) && (highYOffset < 0) && (highZOffset < 0); + + for(int z = volume->getEnclosingRegion().getUpperZ() + highZOffset; z >= volume->getEnclosingRegion().getLowerZ() + lowZOffset; z--) + { + for(int y = volume->getEnclosingRegion().getUpperY() + highYOffset; y >= volume->getEnclosingRegion().getLowerY() + lowYOffset; y--) + { + for(int x = volume->getEnclosingRegion().getUpperX() + highXOffset; x >= volume->getEnclosingRegion().getLowerX() + lowXOffset; x--) + { + //Three level loop now processes 27 voxel neighbourhood + for(int innerZ = -1; innerZ <=1; innerZ++) + { + for(int innerY = -1; innerY <=1; innerY++) + { + for(int innerX = -1; innerX <=1; innerX++) + { + // Deeply nested 'if', but this is just a unit test and we should still + // see some performance improvement by skipping the wrapping versions. + if(bAllVoxelsInternal) + { + result = cantorTupleFunction(result, volume->getVoxel(x + innerX, y + innerY, z + innerZ)); + } + else + { + result = cantorTupleFunction(result, volume->getVoxelWithWrapping(x + innerX, y + innerY, z + innerZ, WrapModes::Border, 3)); + } + } + } + } + //End of inner loops + } + } + } + + return result; +} + +template +int32_t testSamplersWithWrappingBackwards(VolumeType* volume, int lowXOffset, int lowYOffset, int lowZOffset, int highXOffset, int highYOffset, int highZOffset) +{ + int32_t result = 0; + + //Test the sampler move functions + typename VolumeType::Sampler xSampler(volume); + typename VolumeType::Sampler ySampler(volume); + typename VolumeType::Sampler zSampler(volume); + + xSampler.setWrapMode(WrapModes::Border, 3); + ySampler.setWrapMode(WrapModes::Border, 3); + zSampler.setWrapMode(WrapModes::Border, 3); + + zSampler.setPosition(volume->getEnclosingRegion().getUpperX() + highXOffset, volume->getEnclosingRegion().getUpperY() + highYOffset, volume->getEnclosingRegion().getUpperZ() + highZOffset); + for(int z = volume->getEnclosingRegion().getUpperZ() + highZOffset; z >= volume->getEnclosingRegion().getLowerZ() + lowZOffset; z--) + { + ySampler = zSampler; + for(int y = volume->getEnclosingRegion().getUpperY() + highYOffset; y >= volume->getEnclosingRegion().getLowerY() + lowYOffset; y--) + { + xSampler = ySampler; + for(int x = volume->getEnclosingRegion().getUpperX() + highXOffset; x >= volume->getEnclosingRegion().getLowerX() + lowXOffset; x--) + { + result = cantorTupleFunction(result, xSampler.peekVoxel1nx1ny1nz()); + result = cantorTupleFunction(result, xSampler.peekVoxel0px1ny1nz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1px1ny1nz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1nx0py1nz()); + result = cantorTupleFunction(result, xSampler.peekVoxel0px0py1nz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1px0py1nz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1nx1py1nz()); + result = cantorTupleFunction(result, xSampler.peekVoxel0px1py1nz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1px1py1nz()); + + result = cantorTupleFunction(result, xSampler.peekVoxel1nx1ny0pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel0px1ny0pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1px1ny0pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1nx0py0pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel0px0py0pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1px0py0pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1nx1py0pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel0px1py0pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1px1py0pz()); + + result = cantorTupleFunction(result, xSampler.peekVoxel1nx1ny1pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel0px1ny1pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1px1ny1pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1nx0py1pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel0px0py1pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1px0py1pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1nx1py1pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel0px1py1pz()); + result = cantorTupleFunction(result, xSampler.peekVoxel1px1py1pz()); + + xSampler.moveNegativeX(); + } + ySampler.moveNegativeY(); + } + zSampler.moveNegativeZ(); + } + + return result; +} + TestVolume::TestVolume() { Region region(-57, -31, 12, 64, 96, 131); // Deliberatly awkward size @@ -186,138 +301,264 @@ TestVolume::~TestVolume() * RawVolume Tests */ -void TestVolume::testRawVolumeDirectAccessAllInternal() +void TestVolume::testRawVolumeDirectAccessAllInternalForwards() { int32_t result = 0; QBENCHMARK { - result = testDirectAccessWithWrapping(m_pRawVolume, 4, 2, 2, -3, -1, -2); + result = testDirectAccessWithWrappingForwards(m_pRawVolume, 4, 2, 2, -3, -1, -2); } QCOMPARE(result, static_cast(1004598054)); } -void TestVolume::testRawVolumeSamplersAllInternal() +void TestVolume::testRawVolumeSamplersAllInternalForwards() { int32_t result = 0; QBENCHMARK { - result = testSamplersWithWrapping(m_pRawVolume, 4, 2, 2, -3, -1, -2); + result = testSamplersWithWrappingForwards(m_pRawVolume, 4, 2, 2, -3, -1, -2); } QCOMPARE(result, static_cast(1004598054)); } -void TestVolume::testRawVolumeDirectAccessWithExternal() +void TestVolume::testRawVolumeDirectAccessWithExternalForwards() { int32_t result = 0; QBENCHMARK { - result = testDirectAccessWithWrapping(m_pRawVolume, -1, -3, -2, 2, 5, 4); + result = testDirectAccessWithWrappingForwards(m_pRawVolume, -1, -3, -2, 2, 5, 4); } QCOMPARE(result, static_cast(-928601007)); } -void TestVolume::testRawVolumeSamplersWithExternal() +void TestVolume::testRawVolumeSamplersWithExternalForwards() { int32_t result = 0; QBENCHMARK { - result = testSamplersWithWrapping(m_pRawVolume, -1, -3, -2, 2, 5, 4); + result = testSamplersWithWrappingForwards(m_pRawVolume, -1, -3, -2, 2, 5, 4); } QCOMPARE(result, static_cast(-928601007)); } +void TestVolume::testRawVolumeDirectAccessAllInternalBackwards() +{ + int32_t result = 0; + + QBENCHMARK + { + result = testDirectAccessWithWrappingBackwards(m_pRawVolume, 4, 2, 2, -3, -1, -2); + } + QCOMPARE(result, static_cast(-269366578)); +} + +void TestVolume::testRawVolumeSamplersAllInternalBackwards() +{ + int32_t result = 0; + + QBENCHMARK + { + result = testSamplersWithWrappingBackwards(m_pRawVolume, 4, 2, 2, -3, -1, -2); + } + QCOMPARE(result, static_cast(-269366578)); +} + +void TestVolume::testRawVolumeDirectAccessWithExternalBackwards() +{ + int32_t result = 0; + + QBENCHMARK + { + result = testDirectAccessWithWrappingBackwards(m_pRawVolume, -1, -3, -2, 2, 5, 4); + } + QCOMPARE(result, static_cast(-769775893)); +} + +void TestVolume::testRawVolumeSamplersWithExternalBackwards() +{ + int32_t result = 0; + + QBENCHMARK + { + result = testSamplersWithWrappingBackwards(m_pRawVolume, -1, -3, -2, 2, 5, 4); + } + QCOMPARE(result, static_cast(-769775893)); +} + /* * SimpleVolume Tests */ -void TestVolume::testSimpleVolumeDirectAccessAllInternal() +void TestVolume::testSimpleVolumeDirectAccessAllInternalForwards() { int32_t result = 0; QBENCHMARK { - result = testDirectAccessWithWrapping(m_pSimpleVolume, 4, 2, 2, -3, -1, -2); + result = testDirectAccessWithWrappingForwards(m_pSimpleVolume, 4, 2, 2, -3, -1, -2); } QCOMPARE(result, static_cast(1004598054)); } -void TestVolume::testSimpleVolumeSamplersAllInternal() +void TestVolume::testSimpleVolumeSamplersAllInternalForwards() { int32_t result = 0; QBENCHMARK { - result = testSamplersWithWrapping(m_pSimpleVolume, 4, 2, 2, -3, -1, -2); + result = testSamplersWithWrappingForwards(m_pSimpleVolume, 4, 2, 2, -3, -1, -2); } QCOMPARE(result, static_cast(1004598054)); } -void TestVolume::testSimpleVolumeDirectAccessWithExternal() +void TestVolume::testSimpleVolumeDirectAccessWithExternalForwards() { int32_t result = 0; QBENCHMARK { - result = testDirectAccessWithWrapping(m_pSimpleVolume, -1, -3, -2, 2, 5, 4); + result = testDirectAccessWithWrappingForwards(m_pSimpleVolume, -1, -3, -2, 2, 5, 4); } QCOMPARE(result, static_cast(-928601007)); } -void TestVolume::testSimpleVolumeSamplersWithExternal() +void TestVolume::testSimpleVolumeSamplersWithExternalForwards() { int32_t result = 0; QBENCHMARK { - result = testSamplersWithWrapping(m_pSimpleVolume, -1, -3, -2, 2, 5, 4); + result = testSamplersWithWrappingForwards(m_pSimpleVolume, -1, -3, -2, 2, 5, 4); } QCOMPARE(result, static_cast(-928601007)); } +void TestVolume::testSimpleVolumeDirectAccessAllInternalBackwards() +{ + int32_t result = 0; + QBENCHMARK + { + result = testDirectAccessWithWrappingBackwards(m_pSimpleVolume, 4, 2, 2, -3, -1, -2); + } + QCOMPARE(result, static_cast(-269366578)); +} + +void TestVolume::testSimpleVolumeSamplersAllInternalBackwards() +{ + int32_t result = 0; + QBENCHMARK + { + result = testSamplersWithWrappingBackwards(m_pSimpleVolume, 4, 2, 2, -3, -1, -2); + } + QCOMPARE(result, static_cast(-269366578)); +} + +void TestVolume::testSimpleVolumeDirectAccessWithExternalBackwards() +{ + int32_t result = 0; + QBENCHMARK + { + result = testDirectAccessWithWrappingBackwards(m_pSimpleVolume, -1, -3, -2, 2, 5, 4); + } + QCOMPARE(result, static_cast(-769775893)); +} + +void TestVolume::testSimpleVolumeSamplersWithExternalBackwards() +{ + int32_t result = 0; + QBENCHMARK + { + result = testSamplersWithWrappingBackwards(m_pSimpleVolume, -1, -3, -2, 2, 5, 4); + } + QCOMPARE(result, static_cast(-769775893)); +} + /* * LargeVolume Tests */ -void TestVolume::testLargeVolumeDirectAccessAllInternal() +void TestVolume::testLargeVolumeDirectAccessAllInternalForwards() { int32_t result = 0; QBENCHMARK { - result = testDirectAccessWithWrapping(m_pLargeVolume, 4, 2, 2, -3, -1, -2); + result = testDirectAccessWithWrappingForwards(m_pLargeVolume, 4, 2, 2, -3, -1, -2); } QCOMPARE(result, static_cast(1004598054)); } -void TestVolume::testLargeVolumeSamplersAllInternal() +void TestVolume::testLargeVolumeSamplersAllInternalForwards() { int32_t result = 0; QBENCHMARK { - result = testSamplersWithWrapping(m_pLargeVolume, 4, 2, 2, -3, -1, -2); + result = testSamplersWithWrappingForwards(m_pLargeVolume, 4, 2, 2, -3, -1, -2); } QCOMPARE(result, static_cast(1004598054)); } -void TestVolume::testLargeVolumeDirectAccessWithExternal() +void TestVolume::testLargeVolumeDirectAccessWithExternalForwards() { int32_t result = 0; QBENCHMARK { - result = testDirectAccessWithWrapping(m_pLargeVolume, -1, -3, -2, 2, 5, 4); + result = testDirectAccessWithWrappingForwards(m_pLargeVolume, -1, -3, -2, 2, 5, 4); } QCOMPARE(result, static_cast(-928601007)); } -void TestVolume::testLargeVolumeSamplersWithExternal() +void TestVolume::testLargeVolumeSamplersWithExternalForwards() { int32_t result = 0; QBENCHMARK { - result = testSamplersWithWrapping(m_pLargeVolume, -1, -3, -2, 2, 5, 4); + result = testSamplersWithWrappingForwards(m_pLargeVolume, -1, -3, -2, 2, 5, 4); } QCOMPARE(result, static_cast(-928601007)); } +void TestVolume::testLargeVolumeDirectAccessAllInternalBackwards() +{ + int32_t result = 0; + QBENCHMARK + { + result = testDirectAccessWithWrappingBackwards(m_pLargeVolume, 4, 2, 2, -3, -1, -2); + } + QCOMPARE(result, static_cast(-269366578)); +} + +void TestVolume::testLargeVolumeSamplersAllInternalBackwards() +{ + int32_t result = 0; + + QBENCHMARK + { + result = testSamplersWithWrappingBackwards(m_pLargeVolume, 4, 2, 2, -3, -1, -2); + } + QCOMPARE(result, static_cast(-269366578)); +} + +void TestVolume::testLargeVolumeDirectAccessWithExternalBackwards() +{ + int32_t result = 0; + QBENCHMARK + { + result = testDirectAccessWithWrappingBackwards(m_pLargeVolume, -1, -3, -2, 2, 5, 4); + } + QCOMPARE(result, static_cast(-769775893)); +} + +void TestVolume::testLargeVolumeSamplersWithExternalBackwards() +{ + int32_t result = 0; + + QBENCHMARK + { + result = testSamplersWithWrappingBackwards(m_pLargeVolume, -1, -3, -2, 2, 5, 4); + } + QCOMPARE(result, static_cast(-769775893)); +} + QTEST_MAIN(TestVolume) diff --git a/tests/testvolume.h b/tests/testvolume.h index f970918b..18e7ef23 100644 --- a/tests/testvolume.h +++ b/tests/testvolume.h @@ -37,20 +37,32 @@ public: ~TestVolume(); private slots: - void testRawVolumeDirectAccessAllInternal(); - void testRawVolumeSamplersAllInternal(); - void testRawVolumeDirectAccessWithExternal(); - void testRawVolumeSamplersWithExternal(); + void testRawVolumeDirectAccessAllInternalForwards(); + void testRawVolumeSamplersAllInternalForwards(); + void testRawVolumeDirectAccessWithExternalForwards(); + void testRawVolumeSamplersWithExternalForwards(); + void testRawVolumeDirectAccessAllInternalBackwards(); + void testRawVolumeSamplersAllInternalBackwards(); + void testRawVolumeDirectAccessWithExternalBackwards(); + void testRawVolumeSamplersWithExternalBackwards(); - void testSimpleVolumeDirectAccessAllInternal(); - void testSimpleVolumeSamplersAllInternal(); - void testSimpleVolumeDirectAccessWithExternal(); - void testSimpleVolumeSamplersWithExternal(); + void testSimpleVolumeDirectAccessAllInternalForwards(); + void testSimpleVolumeSamplersAllInternalForwards(); + void testSimpleVolumeDirectAccessWithExternalForwards(); + void testSimpleVolumeSamplersWithExternalForwards(); + void testSimpleVolumeDirectAccessAllInternalBackwards(); + void testSimpleVolumeSamplersAllInternalBackwards(); + void testSimpleVolumeDirectAccessWithExternalBackwards(); + void testSimpleVolumeSamplersWithExternalBackwards(); - void testLargeVolumeDirectAccessAllInternal(); - void testLargeVolumeSamplersAllInternal(); - void testLargeVolumeDirectAccessWithExternal(); - void testLargeVolumeSamplersWithExternal(); + void testLargeVolumeDirectAccessAllInternalForwards(); + void testLargeVolumeSamplersAllInternalForwards(); + void testLargeVolumeDirectAccessWithExternalForwards(); + void testLargeVolumeSamplersWithExternalForwards(); + void testLargeVolumeDirectAccessAllInternalBackwards(); + void testLargeVolumeSamplersAllInternalBackwards(); + void testLargeVolumeDirectAccessWithExternalBackwards(); + void testLargeVolumeSamplersWithExternalBackwards(); private: PolyVox::RawVolume* m_pRawVolume; From a0cd1d09b306224b1d82e2dc134e684d7a5920ed Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Thu, 3 Jan 2013 13:23:40 +0100 Subject: [PATCH 12/38] Added a correct set of ADD_TEST macros. Oddly it seems that Visual Studio's 'RUN_TESTS' target doesn't really it care which ADD_TEST macros are used, it just runs all of them. Presumably Linux needs it though. --- tests/CMakeLists.txt | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d76c8167..5634891d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -97,13 +97,32 @@ ADD_TEST(VectorEqualityTest ${LATEST_TEST} testEquality) # Volume tests CREATE_TEST(testvolume.h testvolume.cpp testvolume) -ADD_TEST(RawVolumeDirectAccessTest ${LATEST_TEST} testRawVolumeDirectAccess) -ADD_TEST(SimpleVolumeDirectAccessTest ${LATEST_TEST} testSimpleVolumeDirectAccess) -ADD_TEST(LargeVolumeDirectAccessTest ${LATEST_TEST} testLargeVolumeDirectAccess) +ADD_TEST(RawVolumeDirectAccessAllInternalForwards ${LATEST_TEST} testRawVolumeDirectAccessAllInternalForwards) +ADD_TEST(RawVolumeSamplersAllInternalForwards ${LATEST_TEST} testRawVolumeSamplersAllInternalForwards) +ADD_TEST(RawVolumeDirectAccessWithExternalForwards ${LATEST_TEST} testRawVolumeDirectAccessWithExternalForwards) +ADD_TEST(RawVolumeSamplersWithExternalForwards ${LATEST_TEST} testRawVolumeSamplersWithExternalForwards) +ADD_TEST(RawVolumeDirectAccessAllInternalBackwards ${LATEST_TEST} testRawVolumeDirectAccessAllInternalBackwards) +ADD_TEST(RawVolumeSamplersAllInternalBackwards ${LATEST_TEST} testRawVolumeSamplersAllInternalBackwards) +ADD_TEST(RawVolumeDirectAccessWithExternalBackwards ${LATEST_TEST} testRawVolumeDirectAccessWithExternalBackwards) +ADD_TEST(RawVolumeSamplersWithExternalBackwards ${LATEST_TEST} testRawVolumeSamplersWithExternalBackwards) -ADD_TEST(RawVolumeSamplersTest ${LATEST_TEST} testRawVolumeSamplers) -ADD_TEST(SimpleVolumeSamplersTest ${LATEST_TEST} testSimpleVolumeSamplers) -ADD_TEST(LargeVolumeSamplersTest ${LATEST_TEST} testLargeVolumeSamplers) +ADD_TEST(SimpleVolumeDirectAccessAllInternalForwards ${LATEST_TEST} testSimpleVolumeDirectAccessAllInternalForwards) +ADD_TEST(SimpleVolumeSamplersAllInternalForwards ${LATEST_TEST} testSimpleVolumeSamplersAllInternalForwards) +ADD_TEST(SimpleVolumeDirectAccessWithExternalForwards ${LATEST_TEST} testSimpleVolumeDirectAccessWithExternalForwards) +ADD_TEST(SimpleVolumeSamplersWithExternalForwards ${LATEST_TEST} testSimpleVolumeSamplersWithExternalForwards) +ADD_TEST(SimpleVolumeDirectAccessAllInternalBackwards ${LATEST_TEST} testSimpleVolumeDirectAccessAllInternalBackwards) +ADD_TEST(SimpleVolumeSamplersAllInternalBackwards ${LATEST_TEST} testSimpleVolumeSamplersAllInternalBackwards) +ADD_TEST(SimpleVolumeDirectAccessWithExternalBackwards ${LATEST_TEST} testSimpleVolumeDirectAccessWithExternalBackwards) +ADD_TEST(SimpleVolumeSamplersWithExternalBackwards ${LATEST_TEST} testSimpleVolumeSamplersWithExternalBackwards) + +ADD_TEST(LargeVolumeDirectAccessAllInternalForwards ${LATEST_TEST} testLargeVolumeDirectAccessAllInternalForwards) +ADD_TEST(LargeVolumeSamplersAllInternalForwards ${LATEST_TEST} testLargeVolumeSamplersAllInternalForwards) +ADD_TEST(LargeVolumeDirectAccessWithExternalForwards ${LATEST_TEST} testLargeVolumeDirectAccessWithExternalForwards) +ADD_TEST(LargeVolumeSamplersWithExternalForwards ${LATEST_TEST} testLargeVolumeSamplersWithExternalForwards) +ADD_TEST(LargeVolumeDirectAccessAllInternalBackwards ${LATEST_TEST} testLargeVolumeDirectAccessAllInternalBackwards) +ADD_TEST(LargeVolumeSamplersAllInternalBackwards ${LATEST_TEST} testLargeVolumeSamplersAllInternalBackwards) +ADD_TEST(LargeVolumeDirectAccessWithExternalBackwards ${LATEST_TEST} testLargeVolumeDirectAccessWithExternalBackwards) +ADD_TEST(LargeVolumeSamplersWithExternalBackwards ${LATEST_TEST} testLargeVolumeSamplersWithExternalBackwards) # Volume subclass tests CREATE_TEST(TestVolumeSubclass.h TestVolumeSubclass.cpp TestVolumeSubclass) From 1f466d4931dc53c3d1819e5ad381c200ed6b8c8c Mon Sep 17 00:00:00 2001 From: Matt Williams Date: Fri, 4 Jan 2013 12:02:26 +0000 Subject: [PATCH 13/38] Move this line to make sure the flag is always applied --- library/bindings/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/bindings/CMakeLists.txt b/library/bindings/CMakeLists.txt index 040d0afe..baa2a5f5 100644 --- a/library/bindings/CMakeLists.txt +++ b/library/bindings/CMakeLists.txt @@ -49,8 +49,8 @@ if(ENABLE_BINDINGS) #set_target_properties(${SWIG_MODULE_PolyVoxCore_REAL_NAME} PROPERTIES SUFFIX ".pyd") SET_PROPERTY(TARGET ${SWIG_MODULE_PolyVoxCorePython_REAL_NAME} PROPERTY FOLDER "Bindings") - swig_add_module(PolyVoxCoreCSharp csharp PolyVoxCore.i) set(SWIG_MODULE_PolyVoxCoreCSharp_EXTRA_FLAGS "-dllimport;PolyVoxCoreCSharp") #This _should_ be inside UseSWIG.cmake - http://www.cmake.org/Bug/view.php?id=13814 + swig_add_module(PolyVoxCoreCSharp csharp PolyVoxCore.i) swig_link_libraries(PolyVoxCoreCSharp PolyVoxCore) SET_PROPERTY(TARGET ${SWIG_MODULE_PolyVoxCoreCSharp_REAL_NAME} PROPERTY FOLDER "Bindings") else() From a5d55d4415d859ed4f5e45134dd433185f628e27 Mon Sep 17 00:00:00 2001 From: Matt Williams Date: Fri, 4 Jan 2013 12:03:38 +0000 Subject: [PATCH 14/38] We ignore these operators since they don't exist in C# --- library/bindings/PolyVoxCore.i | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/bindings/PolyVoxCore.i b/library/bindings/PolyVoxCore.i index e5ddfa72..f32ee0e3 100644 --- a/library/bindings/PolyVoxCore.i +++ b/library/bindings/PolyVoxCore.i @@ -61,6 +61,14 @@ EXTRACTOR(shortname, LargeVolume) //This will rename "operator=" to "assign" since Python doesn't have assignment %rename(assign) *::operator=; #endif +#ifdef SWIGCSHARP +//These operators are not wrappable in C# and their function is provided by other means +%ignore *::operator=; +%ignore *::operator+=; +%ignore *::operator-=; +%ignore *::operator*=; +%ignore *::operator/=; +#endif %include "stdint.i" %include "std_vector.i" From a8383b47db7941a41ff802e82d41b487b9e64a72 Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Tue, 8 Jan 2013 16:54:17 +0100 Subject: [PATCH 15/38] Temporarily disabled multiple samplers as they break the LargeVolume tests. --- tests/testvolume.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/testvolume.cpp b/tests/testvolume.cpp index e177d7bc..594558b7 100644 --- a/tests/testvolume.cpp +++ b/tests/testvolume.cpp @@ -110,6 +110,8 @@ int32_t testSamplersWithWrappingForwards(VolumeType* volume, int lowXOffset, int xSampler = ySampler; for(int x = volume->getEnclosingRegion().getLowerX() + lowXOffset; x <= volume->getEnclosingRegion().getUpperX() + highXOffset; x++) { + xSampler.setPosition(x, y, z); // HACK - Accessing a volume through multiple samplers currently breaks the LargeVolume. + result = cantorTupleFunction(result, xSampler.peekVoxel1nx1ny1nz()); result = cantorTupleFunction(result, xSampler.peekVoxel0px1ny1nz()); result = cantorTupleFunction(result, xSampler.peekVoxel1px1ny1nz()); @@ -221,6 +223,8 @@ int32_t testSamplersWithWrappingBackwards(VolumeType* volume, int lowXOffset, in xSampler = ySampler; for(int x = volume->getEnclosingRegion().getUpperX() + highXOffset; x >= volume->getEnclosingRegion().getLowerX() + lowXOffset; x--) { + xSampler.setPosition(x, y, z); // HACK - Accessing a volume through multiple samplers currently breaks the LargeVolume. + result = cantorTupleFunction(result, xSampler.peekVoxel1nx1ny1nz()); result = cantorTupleFunction(result, xSampler.peekVoxel0px1ny1nz()); result = cantorTupleFunction(result, xSampler.peekVoxel1px1ny1nz()); @@ -272,7 +276,7 @@ TestVolume::TestVolume() // LargeVolume currently fails a test if compression is enabled. It // may be related to accessing the data through more than one sampler? - m_pLargeVolume->setCompressionEnabled(false); + //m_pLargeVolume->setCompressionEnabled(false); //Fill the volume with some data for(int z = region.getLowerZ(); z <= region.getUpperZ(); z++) From c7937b176de0fdffcb708f95117b48eb66dd488e Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Thu, 10 Jan 2013 16:20:29 +0100 Subject: [PATCH 16/38] Very bad (but functional!) initial implementation of LargeVolume compression with miniz. --- library/PolyVoxCore/CMakeLists.txt | 2 + .../include/PolyVoxCore/Impl/Block.h | 2 + .../include/PolyVoxCore/Impl/Block.inl | 37 +- .../include/PolyVoxCore/Impl/Compression.h | 38 + .../include/PolyVoxCore/Impl/miniz.c | 4766 +++++++++++++++++ .../polyvoxcore/source/Impl/Compression.cpp | 63 + tests/testvolume.cpp | 8 +- tests/testvolume.h | 4 +- 8 files changed, 4908 insertions(+), 12 deletions(-) create mode 100644 library/PolyVoxCore/include/PolyVoxCore/Impl/Compression.h create mode 100644 library/PolyVoxCore/include/PolyVoxCore/Impl/miniz.c create mode 100644 library/polyvoxcore/source/Impl/Compression.cpp diff --git a/library/PolyVoxCore/CMakeLists.txt b/library/PolyVoxCore/CMakeLists.txt index b0304218..43f95a91 100644 --- a/library/PolyVoxCore/CMakeLists.txt +++ b/library/PolyVoxCore/CMakeLists.txt @@ -94,6 +94,7 @@ SET(CORE_INC_FILES ) SET(IMPL_SRC_FILES + source/Impl/Compression.cpp source/Impl/ErrorHandling.cpp source/Impl/MarchingCubesTables.cpp source/Impl/RandomUnitVectors.cpp @@ -108,6 +109,7 @@ SET(IMPL_INC_FILES include/PolyVoxCore/Impl/Block.h include/PolyVoxCore/Impl/Block.inl include/PolyVoxCore/Impl/CompilerCapabilities.h + include/PolyVoxCore/Impl/Compression.h include/PolyVoxCore/Impl/Config.h include/PolyVoxCore/Impl/ErrorHandling.h include/PolyVoxCore/Impl/MarchingCubesTables.h diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h index bf0b7c90..53e0dd4b 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h @@ -65,6 +65,8 @@ namespace PolyVox void uncompress(void); std::vector< RunlengthEntry > m_vecCompressedData; + uint8_t* m_pCompressedData; + uint32_t m_uCompressedDataLength; VoxelType* m_tUncompressedData; uint16_t m_uSideLength; uint8_t m_uSideLengthPower; diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl index 12184422..4cdef746 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl @@ -21,6 +21,7 @@ freely, subject to the following restrictions: distribution. *******************************************************************************/ +#include "PolyVoxCore/Impl/Compression.h" #include "PolyVoxCore/Impl/ErrorHandling.h" #include "PolyVoxCore/Impl/Utility.h" @@ -36,10 +37,12 @@ namespace PolyVox { template Block::Block(uint16_t uSideLength) - :m_tUncompressedData(0) + :m_pCompressedData(0) + ,m_uCompressedDataLength(0) + ,m_tUncompressedData(0) ,m_uSideLength(0) ,m_uSideLengthPower(0) - ,m_bIsCompressed(true) + ,m_bIsCompressed(false) ,m_bIsUncompressedDataModified(true) { if(uSideLength != 0) @@ -116,6 +119,7 @@ namespace PolyVox } else { + POLYVOX_ASSERT(false, "Not implemented"); RunlengthEntry rle; rle.length = m_uSideLength*m_uSideLength*m_uSideLength; rle.value = tValue; @@ -140,7 +144,11 @@ namespace PolyVox m_uSideLength = uSideLength; m_uSideLengthPower = logBase2(uSideLength); + m_tUncompressedData = new VoxelType[m_uSideLength * m_uSideLength * m_uSideLength]; + Block::fill(VoxelType()); + + compress(); } template @@ -161,7 +169,16 @@ namespace PolyVox //modified then we don't need to redo the compression. if(m_bIsUncompressedDataModified) { - uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength; + Data src; + src.ptr = reinterpret_cast(m_tUncompressedData); + src.length = m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType); + + Data compressedResult = polyvox_compress(src); + + m_pCompressedData = compressedResult.ptr; + m_uCompressedDataLength = compressedResult.length; + + /*uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength; m_vecCompressedData.clear(); RunlengthEntry entry; @@ -188,7 +205,7 @@ namespace PolyVox //Shrink the vectors to their contents (maybe slow?): //http://stackoverflow.com/questions/1111078/reduce-the-capacity-of-an-stl-vector //C++0x may have a shrink_to_fit() function? - std::vector< RunlengthEntry >(m_vecCompressedData).swap(m_vecCompressedData); + std::vector< RunlengthEntry >(m_vecCompressedData).swap(m_vecCompressedData);*/ } //Flag the uncompressed data as no longer being used. @@ -202,14 +219,22 @@ namespace PolyVox { POLYVOX_ASSERT(m_bIsCompressed == true, "Attempted to uncompress block which is not flagged as compressed."); POLYVOX_ASSERT(m_tUncompressedData == 0, "Uncompressed data already exists."); - m_tUncompressedData = new VoxelType[m_uSideLength * m_uSideLength * m_uSideLength]; + /*m_tUncompressedData = new VoxelType[m_uSideLength * m_uSideLength * m_uSideLength]; VoxelType* pUncompressedData = m_tUncompressedData; for(uint32_t ct = 0; ct < m_vecCompressedData.size(); ++ct) { std::fill(pUncompressedData, pUncompressedData + m_vecCompressedData[ct].length, m_vecCompressedData[ct].value); pUncompressedData += m_vecCompressedData[ct].length; - } + }*/ + + Data src; + src.ptr = m_pCompressedData; + src.length = m_uCompressedDataLength; + + Data uncompressedResult = polyvox_decompress(src); + + m_tUncompressedData = reinterpret_cast(uncompressedResult.ptr); m_bIsCompressed = false; m_bIsUncompressedDataModified = false; diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Compression.h b/library/PolyVoxCore/include/PolyVoxCore/Impl/Compression.h new file mode 100644 index 00000000..7d507e6b --- /dev/null +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Compression.h @@ -0,0 +1,38 @@ +/******************************************************************************* +Copyright (c) 2005-2009 David Williams + +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. + +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: + + 1. 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. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*******************************************************************************/ + +#ifndef __PolyVox_Compression_H__ +#define __PolyVox_Compression_H__ + +#include "PolyVoxCore/Impl/TypeDef.h" + +struct Data +{ + uint8_t* ptr; + unsigned long length; // Think what type this should be. +}; + +Data polyvox_compress(Data src); +Data polyvox_decompress(Data src); + +#endif //__PolyVox_Compression_H__ \ No newline at end of file diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/miniz.c b/library/PolyVoxCore/include/PolyVoxCore/Impl/miniz.c new file mode 100644 index 00000000..39a17524 --- /dev/null +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/miniz.c @@ -0,0 +1,4766 @@ +/* miniz.c v1.14 - 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 May 20, 2012 + 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 + 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 +*/ + +#ifndef MINIZ_HEADER_INCLUDED +#define MINIZ_HEADER_INCLUDED + +#include + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) +#include +#endif + +// 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. +//#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(_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 __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; + +// 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_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 + +#define MZ_VERSION "9.1.14" +#define MZ_VERNUM 0x91E0 +#define MZ_VER_MAJOR 9 +#define MZ_VER_MINOR 1 +#define MZ_VER_REVISION 14 +#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) + +// Works 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_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 free() 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. +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. +// 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 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(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 __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; +} + +#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/. +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) +{ + 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, TDEFL_DEFAULT_MAX_PROBES | 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 + 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; + { + 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,"\0\0\04\02\06"[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; +} + +#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__) + #include + #define MZ_FILE FILE + #define MZ_FOPEN 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 freopen + #define MZ_DELETE_FILE remove + #elif defined(__MINGW32__) + #include + #define MZ_FILE FILE + #define MZ_FOPEN fopen + #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 freopen + #define MZ_DELETE_FILE remove + #else + #include + #define MZ_FILE FILE + #define MZ_FOPEN fopen + #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 freopen + #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) +{ + struct tm *tm = localtime(&time); + *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; 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; +} + +static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time) +{ +#ifndef MINIZ_NO_TIME + struct utimbuf t; t.actime = access_time; t.modtime = modified_time; + return !utime(pFilename, &t); +#else + pFilename, access_time, modified_time; + return MZ_TRUE; +#endif // #ifndef MINIZ_NO_TIME +} +#endif + +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; + // 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)) || + (!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); + 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 ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) + 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; + pZip->m_pState->m_pMem = (void *)pMem; + 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)) + 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, internal_attr, external_attr; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) + return MZ_FALSE; + + internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((!internal_attr) && ((external_attr & 0x10) != 0)) + return MZ_TRUE; + + 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; + } + + 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_p)) + 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; + + if (!file_stat.m_comp_size) + 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_uncomp_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; + + if (!file_stat.m_comp_size) + 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 = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, 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; + + 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 = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, 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 ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + + 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; + 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))) + 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)pState->m_central_dir.m_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/library/polyvoxcore/source/Impl/Compression.cpp b/library/polyvoxcore/source/Impl/Compression.cpp new file mode 100644 index 00000000..d94affaa --- /dev/null +++ b/library/polyvoxcore/source/Impl/Compression.cpp @@ -0,0 +1,63 @@ +#include "PolyVoxCore/Impl/Compression.h" +#include "PolyVoxCore/Impl/ErrorHandling.h" + +// The miniz library is supplied only as a single .c file without a header. +// Apparently the only way to use it would then be to #include it directly +// which is what the examples do. +#include "PolyVoxCore/Impl/miniz.c" + +Data polyvox_compress(Data src) +{ + POLYVOX_ASSERT(src.ptr != 0, "Source data cannot be null"); + + unsigned char* buffer; + mz_ulong compressedDataLength = compressBound(src.length); + buffer = new unsigned char[compressedDataLength]; + + int iCompressionResult = compress(buffer, &compressedDataLength, static_cast(src.ptr), src.length); + + // Debug more checking + POLYVOX_ASSERT(iCompressionResult == Z_OK, "Data compression failed."); + + // Release mode checking + if (iCompressionResult != Z_OK) + { + delete[] buffer; + POLYVOX_THROW(std::runtime_error, "Data compression failed."); + } + + Data dst; + dst.length = compressedDataLength; + dst.ptr = new uint8_t[dst.length]; + + memcpy(dst.ptr, buffer, compressedDataLength); + + delete[] buffer; + + return dst; +} + +Data polyvox_decompress(Data src) +{ + unsigned char* buffer; + mz_ulong uncompressedDataLength = 1000000; + buffer = new unsigned char[uncompressedDataLength]; + + int iUncompressionResult = uncompress(buffer, &uncompressedDataLength, src.ptr, src.length); + + if (iUncompressionResult != Z_OK) + { + delete[] buffer; + POLYVOX_THROW(std::runtime_error, "Data decompression failed."); + } + + Data dst; + dst.length = uncompressedDataLength; + dst.ptr = new uint8_t[dst.length]; + + memcpy(dst.ptr, buffer, uncompressedDataLength); + + delete[] buffer; + + return dst; +} \ No newline at end of file diff --git a/tests/testvolume.cpp b/tests/testvolume.cpp index 594558b7..cdb9fbf5 100644 --- a/tests/testvolume.cpp +++ b/tests/testvolume.cpp @@ -305,7 +305,7 @@ TestVolume::~TestVolume() * RawVolume Tests */ -void TestVolume::testRawVolumeDirectAccessAllInternalForwards() +/*void TestVolume::testRawVolumeDirectAccessAllInternalForwards() { int32_t result = 0; @@ -391,13 +391,13 @@ void TestVolume::testRawVolumeSamplersWithExternalBackwards() result = testSamplersWithWrappingBackwards(m_pRawVolume, -1, -3, -2, 2, 5, 4); } QCOMPARE(result, static_cast(-769775893)); -} +}*/ /* * SimpleVolume Tests */ -void TestVolume::testSimpleVolumeDirectAccessAllInternalForwards() +/*void TestVolume::testSimpleVolumeDirectAccessAllInternalForwards() { int32_t result = 0; QBENCHMARK @@ -475,7 +475,7 @@ void TestVolume::testSimpleVolumeSamplersWithExternalBackwards() result = testSamplersWithWrappingBackwards(m_pSimpleVolume, -1, -3, -2, 2, 5, 4); } QCOMPARE(result, static_cast(-769775893)); -} +}*/ /* * LargeVolume Tests diff --git a/tests/testvolume.h b/tests/testvolume.h index 18e7ef23..7917f577 100644 --- a/tests/testvolume.h +++ b/tests/testvolume.h @@ -37,7 +37,7 @@ public: ~TestVolume(); private slots: - void testRawVolumeDirectAccessAllInternalForwards(); + /*void testRawVolumeDirectAccessAllInternalForwards(); void testRawVolumeSamplersAllInternalForwards(); void testRawVolumeDirectAccessWithExternalForwards(); void testRawVolumeSamplersWithExternalForwards(); @@ -53,7 +53,7 @@ private slots: void testSimpleVolumeDirectAccessAllInternalBackwards(); void testSimpleVolumeSamplersAllInternalBackwards(); void testSimpleVolumeDirectAccessWithExternalBackwards(); - void testSimpleVolumeSamplersWithExternalBackwards(); + void testSimpleVolumeSamplersWithExternalBackwards();*/ void testLargeVolumeDirectAccessAllInternalForwards(); void testLargeVolumeSamplersAllInternalForwards(); From 7bb7be0dec4d468c1544ee115817cad98857c6a9 Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Thu, 10 Jan 2013 16:37:02 +0100 Subject: [PATCH 17/38] Work on block compression. --- .../include/PolyVoxCore/Impl/Block.h | 1 - .../include/PolyVoxCore/Impl/Block.inl | 30 ++++--------------- 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h index 53e0dd4b..3c253186 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h @@ -56,7 +56,6 @@ namespace PolyVox void setVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos, VoxelType tValue); void setVoxelAt(const Vector3DUint16& v3dPos, VoxelType tValue); - void fill(VoxelType tValue); void initialise(uint16_t uSideLength); uint32_t calculateSizeInBytes(void); diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl index 4cdef746..2849b838 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl @@ -105,29 +105,6 @@ namespace PolyVox setVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tValue); } - template - void Block::fill(VoxelType tValue) - { - if(!m_bIsCompressed) - { - //The memset *may* be faster than the std::fill(), but it doesn't compile nicely - //in 64-bit mode as casting the pointer to an int causes a loss of precision. - const uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength; - std::fill(m_tUncompressedData, m_tUncompressedData + uNoOfVoxels, tValue); - - m_bIsUncompressedDataModified = true; - } - else - { - POLYVOX_ASSERT(false, "Not implemented"); - RunlengthEntry rle; - rle.length = m_uSideLength*m_uSideLength*m_uSideLength; - rle.value = tValue; - m_vecCompressedData.clear(); - m_vecCompressedData.push_back(rle); - } - } - template void Block::initialise(uint16_t uSideLength) { @@ -144,10 +121,15 @@ namespace PolyVox m_uSideLength = uSideLength; m_uSideLengthPower = logBase2(uSideLength); + //Create the block data m_tUncompressedData = new VoxelType[m_uSideLength * m_uSideLength * m_uSideLength]; - Block::fill(VoxelType()); + //Clear it (should we bother?) + const uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength; + std::fill(m_tUncompressedData, m_tUncompressedData + uNoOfVoxels, VoxelType()); + m_bIsUncompressedDataModified = true; + //For some reason blocks start out compressed. We should probably change this. compress(); } From 68ee094cecd21858e3fcc89c4328bec093855075 Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Fri, 11 Jan 2013 13:29:33 +0100 Subject: [PATCH 18/38] More work on block compression with miniz. --- .../include/PolyVoxCore/Impl/Block.inl | 12 ++++++++++-- .../include/PolyVoxCore/Impl/Compression.h | 2 +- library/polyvoxcore/source/Impl/Compression.cpp | 16 ++++++++-------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl index 2849b838..65325b65 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl @@ -210,13 +210,21 @@ namespace PolyVox pUncompressedData += m_vecCompressedData[ct].length; }*/ + m_tUncompressedData = new VoxelType[m_uSideLength * m_uSideLength * m_uSideLength]; + Data src; src.ptr = m_pCompressedData; src.length = m_uCompressedDataLength; - Data uncompressedResult = polyvox_decompress(src); + Data dst; + dst.ptr = reinterpret_cast(m_tUncompressedData); + dst.length = m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType); - m_tUncompressedData = reinterpret_cast(uncompressedResult.ptr); + polyvox_decompress(src, dst); + + POLYVOX_ASSERT(dst.length == m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType), "Destination length has changed."); + + //m_tUncompressedData = reinterpret_cast(uncompressedResult.ptr); m_bIsCompressed = false; m_bIsUncompressedDataModified = false; diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Compression.h b/library/PolyVoxCore/include/PolyVoxCore/Impl/Compression.h index 7d507e6b..6c98b4a0 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Compression.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Compression.h @@ -33,6 +33,6 @@ struct Data }; Data polyvox_compress(Data src); -Data polyvox_decompress(Data src); +void polyvox_decompress(Data src, Data dst); #endif //__PolyVox_Compression_H__ \ No newline at end of file diff --git a/library/polyvoxcore/source/Impl/Compression.cpp b/library/polyvoxcore/source/Impl/Compression.cpp index d94affaa..d533b430 100644 --- a/library/polyvoxcore/source/Impl/Compression.cpp +++ b/library/polyvoxcore/source/Impl/Compression.cpp @@ -37,27 +37,27 @@ Data polyvox_compress(Data src) return dst; } -Data polyvox_decompress(Data src) +void polyvox_decompress(Data src, Data dst) { - unsigned char* buffer; + /*unsigned char* buffer; mz_ulong uncompressedDataLength = 1000000; - buffer = new unsigned char[uncompressedDataLength]; + buffer = new unsigned char[uncompressedDataLength];*/ - int iUncompressionResult = uncompress(buffer, &uncompressedDataLength, src.ptr, src.length); + int iUncompressionResult = uncompress(dst.ptr, &dst.length, src.ptr, src.length); if (iUncompressionResult != Z_OK) { - delete[] buffer; + //delete[] buffer; POLYVOX_THROW(std::runtime_error, "Data decompression failed."); } - Data dst; + /*Data dst; dst.length = uncompressedDataLength; dst.ptr = new uint8_t[dst.length]; memcpy(dst.ptr, buffer, uncompressedDataLength); - delete[] buffer; + delete[] buffer;*/ - return dst; + //return dst; } \ No newline at end of file From 7b64c0c3c082505ca9a430c2ff3ad35536a34056 Mon Sep 17 00:00:00 2001 From: Matt Williams Date: Mon, 14 Jan 2013 12:27:04 +0000 Subject: [PATCH 19/38] Move Compression.cpp to folder with correct case --- library/{polyvoxcore => PolyVoxCore}/source/Impl/Compression.cpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename library/{polyvoxcore => PolyVoxCore}/source/Impl/Compression.cpp (100%) diff --git a/library/polyvoxcore/source/Impl/Compression.cpp b/library/PolyVoxCore/source/Impl/Compression.cpp similarity index 100% rename from library/polyvoxcore/source/Impl/Compression.cpp rename to library/PolyVoxCore/source/Impl/Compression.cpp From e90215b0fc4b55859501a8716e82e576ec664f04 Mon Sep 17 00:00:00 2001 From: Matt Williams Date: Wed, 16 Jan 2013 15:29:17 +0000 Subject: [PATCH 20/38] These names only make sense for Python In future, it might make sense to use these names as the intermediate names for C# and Java too but for now, keep them separate. --- library/bindings/Vector.i | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/bindings/Vector.i b/library/bindings/Vector.i index 37d0b3ab..188333e7 100644 --- a/library/bindings/Vector.i +++ b/library/bindings/Vector.i @@ -13,6 +13,7 @@ PROPERTY(PolyVox::Vector, z, getZ, setZ) #endif %extend PolyVox::Vector { +#ifdef SWIGPYTHON PolyVox::Vector __add__(const PolyVox::Vector& rhs) { return *$self + rhs; } @@ -31,6 +32,7 @@ PROPERTY(PolyVox::Vector, z, getZ, setZ) PolyVox::Vector __mul__(const StorageType& rhs) { return *$self * rhs; } +#endif STR() }; From bab3c32ec5a38c0c4d8ec9055d082f5891490093 Mon Sep 17 00:00:00 2001 From: Matt Williams Date: Wed, 16 Jan 2013 15:30:22 +0000 Subject: [PATCH 21/38] Wrap the Vector operators for C# This should allow all the normal vector operations as well as silence the warnings from SWIG. --- library/bindings/Vector.i | 69 +++++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 6 deletions(-) diff --git a/library/bindings/Vector.i b/library/bindings/Vector.i index 188333e7..ac6eed46 100644 --- a/library/bindings/Vector.i +++ b/library/bindings/Vector.i @@ -12,6 +12,13 @@ PROPERTY(PolyVox::Vector, y, getY, setY) PROPERTY(PolyVox::Vector, z, getZ, setZ) #endif +%rename(Plus) operator +; +%rename(Minus) operator -; +%rename(Multiply) operator *; +%rename(Divide) operator /; +%rename(Equal) operator ==; +%rename(NotEqual) operator !=; + %extend PolyVox::Vector { #ifdef SWIGPYTHON PolyVox::Vector __add__(const PolyVox::Vector& rhs) { @@ -44,12 +51,62 @@ PROPERTY(PolyVox::Vector, z, getZ, setZ) //%csattributes PolyVox::Vector::operator< "[System.Obsolete(\"deprecated\")]" %define VECTOR3(StorageType,OperationType,ReducedStorageType) -%ignore PolyVox::Vector<3,StorageType,OperationType>::Vector(ReducedStorageType,ReducedStorageType,ReducedStorageType,ReducedStorageType); -%ignore PolyVox::Vector<3,StorageType,OperationType>::Vector(ReducedStorageType,ReducedStorageType); -%ignore PolyVox::Vector<3,StorageType,OperationType>::getW() const; -%ignore PolyVox::Vector<3,StorageType,OperationType>::setW(ReducedStorageType); -%ignore PolyVox::Vector<3,StorageType,OperationType>::setElements(ReducedStorageType,ReducedStorageType,ReducedStorageType,ReducedStorageType); -%template(Vector3D ## StorageType) PolyVox::Vector<3,StorageType,OperationType>; + #if SWIGCSHARP + %extend PolyVox::Vector<3,StorageType,OperationType> { + PolyVox::Vector<3,StorageType,OperationType> operator+(const PolyVox::Vector<3,StorageType,OperationType>& rhs) {return *$self + rhs;} + PolyVox::Vector<3,StorageType,OperationType> operator-(const PolyVox::Vector<3,StorageType,OperationType>& rhs) {return *$self - rhs;} + PolyVox::Vector<3,StorageType,OperationType> operator*(const PolyVox::Vector<3,StorageType,OperationType>& rhs) {return *$self * rhs;} + PolyVox::Vector<3,StorageType,OperationType> operator/(const PolyVox::Vector<3,StorageType,OperationType>& rhs) {return *$self / rhs;} + PolyVox::Vector<3,StorageType,OperationType> operator*(const StorageType& rhs) {return *$self * rhs;} + PolyVox::Vector<3,StorageType,OperationType> operator/(const StorageType& rhs) {return *$self / rhs;} + }; + %typemap(cscode) PolyVox::Vector<3,StorageType,OperationType> %{ + public static Vector3D##StorageType operator+(Vector3D##StorageType lhs, Vector3D##StorageType rhs) { + Vector3D##StorageType newVec = new Vector3D##StorageType(); + newVec = lhs.Plus(rhs); + return newVec; + } + public static Vector3D##StorageType operator-(Vector3D##StorageType lhs, Vector3D##StorageType rhs) { + Vector3D##StorageType newVec = new Vector3D##StorageType(); + newVec = lhs.Minus(rhs); + return newVec; + } + public static Vector3D##StorageType operator*(Vector3D##StorageType lhs, Vector3D##StorageType rhs) { + Vector3D##StorageType newVec = new Vector3D##StorageType(); + newVec = lhs.Multiply(rhs); + return newVec; + } + public static Vector3D##StorageType operator/(Vector3D##StorageType lhs, Vector3D##StorageType rhs) { + Vector3D##StorageType newVec = new Vector3D##StorageType(); + newVec = lhs.Divide(rhs); + return newVec; + } + public static Vector3D##StorageType operator*(Vector3D##StorageType lhs, $typemap(cstype, StorageType) rhs) { + Vector3D##StorageType newVec = new Vector3D##StorageType(); + newVec = lhs.Multiply(rhs); + return newVec; + } + public static Vector3D##StorageType operator/(Vector3D##StorageType lhs, $typemap(cstype, StorageType) rhs) { + Vector3D##StorageType newVec = new Vector3D##StorageType(); + newVec = lhs.Divide(rhs); + return newVec; + } + public bool Equals(Vector3D##StorageType rhs) { + if ((object)rhs == null) + { + return false; + } + return Equal(rhs); + } + %} + %ignore PolyVox::Vector<3,StorageType,OperationType>::operator<; //This is deprecated + #endif + %ignore PolyVox::Vector<3,StorageType,OperationType>::Vector(ReducedStorageType,ReducedStorageType,ReducedStorageType,ReducedStorageType); + %ignore PolyVox::Vector<3,StorageType,OperationType>::Vector(ReducedStorageType,ReducedStorageType); + %ignore PolyVox::Vector<3,StorageType,OperationType>::getW() const; + %ignore PolyVox::Vector<3,StorageType,OperationType>::setW(ReducedStorageType); + %ignore PolyVox::Vector<3,StorageType,OperationType>::setElements(ReducedStorageType,ReducedStorageType,ReducedStorageType,ReducedStorageType); + %template(Vector3D ## StorageType) PolyVox::Vector<3,StorageType,OperationType>; %enddef VECTOR3(float,float,float) From 6e93048c9f5321d7f73eedd8e0909ef938cd556d Mon Sep 17 00:00:00 2001 From: Matt Williams Date: Wed, 16 Jan 2013 15:31:52 +0000 Subject: [PATCH 22/38] Add a test for C# This test is not actually run yet but it serves as an example of how to use the C# bindings. --- tests/TestSurfaceExtractor.cs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tests/TestSurfaceExtractor.cs diff --git a/tests/TestSurfaceExtractor.cs b/tests/TestSurfaceExtractor.cs new file mode 100644 index 00000000..58e6cc60 --- /dev/null +++ b/tests/TestSurfaceExtractor.cs @@ -0,0 +1,32 @@ +using System.Diagnostics; + +public class test +{ + public static void Main() + { + Region r = new Region(new Vector3Dint32_t(0,0,0), new Vector3Dint32_t(31,31,31)); + SimpleVolumeuint8 vol = new SimpleVolumeuint8(r); + //Set one single voxel to have a reasonably high density + vol.setVoxelAt(new Vector3Dint32_t(5, 5, 5), 200); + SurfaceMeshPositionMaterialNormal mesh = new SurfaceMeshPositionMaterialNormal(); + MarchingCubesSurfaceExtractorSimpleVolumeuint8 extractor = new MarchingCubesSurfaceExtractorSimpleVolumeuint8(vol, r, mesh); + extractor.execute(); + + Debug.Assert(mesh.getNoOfVertices() == 6); + + Vector3Dint32_t v1 = new Vector3Dint32_t(1,2,3); + Vector3Dint32_t v2 = new Vector3Dint32_t(6,8,12); + Vector3Dint32_t v3 = v1 + v2; + + Vector3Dint32_t v11 = new Vector3Dint32_t(1,2,3); + + Debug.Assert(v3.getX() == 7); + Debug.Assert((v3*5).getX() == 35); + Debug.Assert(v1.Equals(v11)); + Debug.Assert(v1 != v11); + Debug.Assert(!v1.Equals(v2)); + Debug.Assert(!v1.Equals(null)); + + System.Console.WriteLine("Success"); + } +} From d50e9dfebc135098d0048585ec29da441b4c59b8 Mon Sep 17 00:00:00 2001 From: Matt Williams Date: Sat, 19 Jan 2013 14:19:26 +0000 Subject: [PATCH 23/38] Start enabling the CubicSurfaceExtractors --- library/bindings/CubicSurfaceExtractor.i | 3 ++- library/bindings/CubicSurfaceExtractorWithNormals.i | 7 +------ library/bindings/PolyVoxCore.i | 3 ++- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/library/bindings/CubicSurfaceExtractor.i b/library/bindings/CubicSurfaceExtractor.i index 8c17997e..11e76cdb 100644 --- a/library/bindings/CubicSurfaceExtractor.i +++ b/library/bindings/CubicSurfaceExtractor.i @@ -5,4 +5,5 @@ %include "CubicSurfaceExtractor.h" -EXTRACTORS(CubicSurfaceExtractor) +%template(CubicSurfaceExtractorSimpleVolumeuint8) PolyVox::CubicSurfaceExtractor, PolyVox::DefaultIsQuadNeeded >; +//EXTRACTORS(CubicSurfaceExtractor) diff --git a/library/bindings/CubicSurfaceExtractorWithNormals.i b/library/bindings/CubicSurfaceExtractorWithNormals.i index 93d778cb..5bd6e0ab 100644 --- a/library/bindings/CubicSurfaceExtractorWithNormals.i +++ b/library/bindings/CubicSurfaceExtractorWithNormals.i @@ -1,13 +1,8 @@ %module CubicSurfaceExtractorWithNormals %{ -#include "SimpleVolume.h" -//#include "CubicSurfaceExtractor.h" #include "CubicSurfaceExtractorWithNormals.h" %} -%include "SimpleVolume.h" -//%include "CubicSurfaceExtractor.h" %include "CubicSurfaceExtractorWithNormals.h" -%template(CubicSurfaceExtractorWithNormalsMaterial8) PolyVox::CubicSurfaceExtractorWithNormals; -%template(CubicSurfaceExtractorWithNormalsDensity8) PolyVox::CubicSurfaceExtractorWithNormals; \ No newline at end of file +%template(CubicSurfaceExtractorWithNormalsSimpleVolumeuint8) PolyVox::CubicSurfaceExtractorWithNormals, PolyVox::DefaultIsQuadNeeded >; diff --git a/library/bindings/PolyVoxCore.i b/library/bindings/PolyVoxCore.i index f32ee0e3..8c0d30c4 100644 --- a/library/bindings/PolyVoxCore.i +++ b/library/bindings/PolyVoxCore.i @@ -84,5 +84,6 @@ EXTRACTOR(shortname, LargeVolume) %include "VertexTypes.i" %include "SurfaceMesh.i" %include "MarchingCubesSurfaceExtractor.i" -//%include "CubicSurfaceExtractor.i" +%include "CubicSurfaceExtractor.i" +%include "CubicSurfaceExtractorWithNormals.i" %include "Raycast.i" From 8ad0cae89a37200b8789c3e7dc393d27e9b89e64 Mon Sep 17 00:00:00 2001 From: Matt Williams Date: Sat, 19 Jan 2013 14:24:10 +0000 Subject: [PATCH 24/38] Add an example which uses the Python bindings This is more or less a copy of the BasicExample but using ever so slightly more modern OpenGL (>=3.0). I've tried to comment this as much as possible. In addition to this simple example I will go on to develop a more complex application as discussed in issue #21 --- CMakeLists.txt | 1 + examples/Python/CMakeLists.txt | 26 ++++ examples/Python/PythonExample.py | 257 +++++++++++++++++++++++++++++++ 3 files changed, 284 insertions(+) create mode 100644 examples/Python/CMakeLists.txt create mode 100755 examples/Python/PythonExample.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 8af58ba6..61384b2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,7 @@ IF(ENABLE_EXAMPLES AND QT_QTOPENGL_FOUND) ADD_SUBDIRECTORY(examples/Paging) ADD_SUBDIRECTORY(examples/OpenGL) ADD_SUBDIRECTORY(examples/SmoothLOD) + ADD_SUBDIRECTORY(examples/Python) SET(BUILD_EXAMPLES ON) ELSE() SET(BUILD_EXAMPLES OFF) diff --git a/examples/Python/CMakeLists.txt b/examples/Python/CMakeLists.txt new file mode 100644 index 00000000..1d101d6e --- /dev/null +++ b/examples/Python/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (c) 2010-2013 Matt Williams +# +# 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. +# +# 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: +# +# 1. 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. +# +# 2. Altered source versions must be plainly marked as such, and must not be +# misrepresented as being the original software. +# +# 3. This notice may not be removed or altered from any source +# distribution. + +PROJECT(PythonExample) + +SOURCE_GROUP("Sources" FILES PythonExample.py) + +configure_file(PythonExample.py PythonExample.py COPYONLY) diff --git a/examples/Python/PythonExample.py b/examples/Python/PythonExample.py new file mode 100755 index 00000000..2a688971 --- /dev/null +++ b/examples/Python/PythonExample.py @@ -0,0 +1,257 @@ +#! /usr/bin/env python + +################################################################################ +# Copyright (c) 2013 Matt Williams +# +# 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. +# +# 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: +# +# 1. 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. +# +# 2. Altered source versions must be plainly marked as such, and must not be +# misrepresented as being the original software. +# +# 3. This notice may not be removed or altered from any source +# distribution. +################################################################################ + +import sys +sys.path.append("../../library/bindings/") #This is just to point to the generated bindings + +import PolyVoxCore as pv + +#Create a 64x64x64 volume of integers +r = pv.Region(pv.Vector3Dint32_t(0,0,0), pv.Vector3Dint32_t(63,63,63)) +vol = pv.SimpleVolumeuint8(r) + +#Now fill the volume with our data (a sphere) +v3dVolCenter = pv.Vector3Dint32_t(vol.getWidth() / 2, vol.getHeight() / 2, vol.getDepth() / 2) +sphereRadius = 30 +#This three-level for loop iterates over every voxel in the volume +for z in range(vol.getDepth()): + for y in range(vol.getHeight()): + for x in range(vol.getWidth()): + #Store our current position as a vector... + v3dCurrentPos = pv.Vector3Dint32_t(x,y,z) + #And compute how far the current position is from the center of the volume + fDistToCenter = (v3dCurrentPos - v3dVolCenter).length() + + uVoxelValue = 0 + + #If the current voxel is less than 'radius' units from the center then we make it solid. + if(fDistToCenter <= sphereRadius): + #Our new voxel value + uVoxelValue = 255 + + #Write the voxel value into the volume + vol.setVoxelAt(x, y, z, uVoxelValue); + +#Create a mesh, pass it to the extractor and generate the mesh +mesh = pv.SurfaceMeshPositionMaterialNormal() +extractor = pv.CubicSurfaceExtractorWithNormalsSimpleVolumeuint8(vol, r, mesh) +extractor.execute() + +#That's all of the PolyVox generation done, now to convert the output to something OpenGL can read efficiently + +import numpy as np + +indices = np.array(mesh.getIndices(), dtype='uint32') #Throw in the vertex indices into an array +#The vertices and normals are placed in an interpolated array like [vvvnnn,vvvnnn,vvvnnn] +vertices = np.array([[vertex.getPosition().getX(), vertex.getPosition().getY(), vertex.getPosition().getZ(), + vertex.getNormal().getX(), vertex.getNormal().getY(), vertex.getNormal().getZ()] + for vertex in mesh.getVertices()], + dtype='f') + +#Now that we have our data, everything else here is just OpenGL + +import OpenGL + +from OpenGL.GL import shaders +from OpenGL.arrays import vbo +from OpenGL.GL import glClear, glEnable, glDepthFunc, GLuint, glEnableVertexAttribArray, glVertexAttribPointer, glDisableVertexAttribArray, \ + glDrawElements, glGetUniformLocation, glUniformMatrix4fv, glDepthMask, glDepthRange, glGetString, glBindAttribLocation, \ + GL_COLOR_BUFFER_BIT, GL_TRIANGLES, GL_DEPTH_TEST, GL_LEQUAL, GL_FLOAT, \ + GL_DEPTH_BUFFER_BIT, GL_ELEMENT_ARRAY_BUFFER, GL_UNSIGNED_INT, GL_STATIC_DRAW, \ + GL_FALSE, GL_TRUE, GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, GL_CULL_FACE, \ + GL_VENDOR, GL_RENDERER, GL_VERSION, GL_SHADING_LANGUAGE_VERSION +from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, glBindVertexArray + +import pygame + +from math import sin, cos, tan, radians + +SCREEN_SIZE = (800, 800) + +def run(): + #Start OpenGL and ask it for an OpenGL context + pygame.init() + clock = pygame.time.Clock() + screen = pygame.display.set_mode(SCREEN_SIZE, pygame.HWSURFACE|pygame.OPENGL|pygame.DOUBLEBUF) + + #The first thing we do is print some OpenGL details and check that we have a good enough version + print "OpenGL Implementation Details:" + if glGetString(GL_VENDOR): + print "\tGL_VENDOR:", glGetString(GL_VENDOR) + if glGetString(GL_RENDERER): + print "\tGL_RENDERER:", glGetString(GL_RENDERER) + if glGetString(GL_VERSION): + print "\tGL_VERSION:", glGetString(GL_VERSION) + if glGetString(GL_SHADING_LANGUAGE_VERSION): + print "\tGL_SHADING_LANGUAGE_VERSION:", glGetString(GL_SHADING_LANGUAGE_VERSION) + + major_version = int(glGetString(GL_VERSION).split()[0].split('.')[0]) + minor_version = int(glGetString(GL_VERSION).split()[0].split('.')[1]) + if major_version < 3 or (major_version < 3 and minor_version < 0): + print "OpenGL version must be at least 3.0 (found {0})".format(glGetString(GL_VERSION).split()[0]) + + #Now onto the OpenGL initialisation + + #Set up depth culling + glEnable(GL_CULL_FACE) + glEnable(GL_DEPTH_TEST) + glDepthMask(GL_TRUE) + glDepthFunc(GL_LEQUAL) + glDepthRange(0.0, 1.0) + + #We create out shaders which do little more than set a flat colour for each face + + VERTEX_SHADER = shaders.compileShader(""" + #version 130 + + in vec4 position; + in vec4 normal; + + uniform mat4 cameraToClipMatrix; + uniform mat4 worldToCameraMatrix; + uniform mat4 modelToWorldMatrix; + + flat out float theColor; + + void main() + { + vec4 temp = modelToWorldMatrix * position; + temp = worldToCameraMatrix * temp; + gl_Position = cameraToClipMatrix * temp; + + theColor = clamp(abs(dot(normalize(normal.xyz), normalize(vec3(0.9,0.1,0.5)))), 0, 1); + } + """, GL_VERTEX_SHADER) + + FRAGMENT_SHADER = shaders.compileShader(""" + #version 130 + + flat in float theColor; + + out vec4 outputColor; + void main() + { + outputColor = vec4(1.0, 0.5, theColor, 1.0); + } + """, GL_FRAGMENT_SHADER) + + shader = shaders.compileProgram(VERTEX_SHADER, FRAGMENT_SHADER) + + #And then grab our attribute locations from it + glBindAttribLocation(shader, 0, "position") + glBindAttribLocation(shader, 1, "normal") + + #Create the Vertex Array Object to hold our volume mesh + vertexArrayObject = GLuint(0) + glGenVertexArrays(1, vertexArrayObject) + glBindVertexArray(vertexArrayObject) + + #Create the index buffer object + indexPositions = vbo.VBO(indices, target=GL_ELEMENT_ARRAY_BUFFER, usage=GL_STATIC_DRAW) + #Create the VBO + vertexPositions = vbo.VBO(vertices, usage=GL_STATIC_DRAW) + + #Bind our VBOs and set up our data layout specifications + with indexPositions, vertexPositions: + glEnableVertexAttribArray(0) + glVertexAttribPointer(0, 3, GL_FLOAT, False, 6*vertices.dtype.itemsize, vertexPositions+(0*vertices.dtype.itemsize)) + glEnableVertexAttribArray(1) + glVertexAttribPointer(1, 3, GL_FLOAT, False, 6*vertices.dtype.itemsize, vertexPositions+(3*vertices.dtype.itemsize)) + + glBindVertexArray(0) + glDisableVertexAttribArray(0) + + #Now grab out transformation martix locations + modelToWorldMatrixUnif = glGetUniformLocation(shader, "modelToWorldMatrix") + worldToCameraMatrixUnif = glGetUniformLocation(shader, "worldToCameraMatrix") + cameraToClipMatrixUnif = glGetUniformLocation(shader, "cameraToClipMatrix") + + modelToWorldMatrix = np.array([[1.0,0.0,0.0,-32.0],[0.0,1.0,0.0,-32.0],[0.0,0.0,1.0,-32.0],[0.0,0.0,0.0,1.0]], dtype='f') + worldToCameraMatrix = np.array([[1.0,0.0,0.0,0.0],[0.0,1.0,0.0,0.0],[0.0,0.0,1.0,-50.0],[0.0,0.0,0.0,1.0]], dtype='f') + cameraToClipMatrix = np.array([[0.0,0.0,0.0,0.0],[0.0,0.0,0.0,0.0],[0.0,0.0,0.0,0.0],[0.0,0.0,0.0,0.0]], dtype='f') + + #These next few lines just set up our camera frustum + fovDeg = 45.0 + frustumScale = 1.0 / tan(radians(fovDeg) / 2.0) + + zNear = 1.0 + zFar = 1000.0 + + cameraToClipMatrix[0][0] = frustumScale + cameraToClipMatrix[1][1] = frustumScale + cameraToClipMatrix[2][2] = (zFar + zNear) / (zNear - zFar) + cameraToClipMatrix[2][3] = -1.0 + cameraToClipMatrix[3][2] = (2 * zFar * zNear) / (zNear - zFar) + + #worldToCameraMatrix and cameraToClipMatrix don't change ever so just set them once here + with shader: + glUniformMatrix4fv(cameraToClipMatrixUnif, 1, GL_TRUE, cameraToClipMatrix) + glUniformMatrix4fv(worldToCameraMatrixUnif, 1, GL_TRUE, worldToCameraMatrix) + + #These are used to track the rotation of the volume + LastFrameMousePos = (0,0) + CurrentMousePos = (0,0) + xRotation = 0 + yRotation = 0 + + while True: + clock.tick() + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + return + if event.type == pygame.KEYUP and event.key == pygame.K_ESCAPE: + return + if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: + CurrentMousePos = event.pos + LastFrameMousePos = CurrentMousePos + if event.type == pygame.MOUSEMOTION and 1 in event.buttons: + CurrentMousePos = event.pos + diff = (CurrentMousePos[0] - LastFrameMousePos[0], CurrentMousePos[1] - LastFrameMousePos[1]) + xRotation += event.rel[0] + yRotation += event.rel[1] + LastFrameMousePos = CurrentMousePos + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) + + #Perform the rotation of the mesh + moveToOrigin = np.array([[1.0,0.0,0.0,-32.0],[0.0,1.0,0.0,-32.0],[0.0,0.0,1.0,-32.0],[0.0,0.0,0.0,1.0]], dtype='f') + rotateAroundX = np.array([[1.0,0.0,0.0,0.0],[0.0,cos(radians(yRotation)),-sin(radians(yRotation)),0.0],[0.0,sin(radians(yRotation)),cos(radians(yRotation)),0.0],[0.0,0.0,0.0,1.0]], dtype='f') + rotateAroundY = np.array([[cos(radians(xRotation)),0.0,sin(radians(xRotation)),0.0],[0.0,1.0,0.0,0.0],[-sin(radians(xRotation)),0.0,cos(radians(xRotation)),0.0],[0.0,0.0,0.0,1.0]], dtype='f') + + modelToWorldMatrix = rotateAroundY.dot(rotateAroundX.dot(moveToOrigin)) + + with shader: + glUniformMatrix4fv(modelToWorldMatrixUnif, 1, GL_TRUE, modelToWorldMatrix) + glBindVertexArray(vertexArrayObject) + + glDrawElements(GL_TRIANGLES, len(indices), GL_UNSIGNED_INT, None) + + glBindVertexArray(0) + + # Show the screen + pygame.display.flip() + +run() From 804a766037ccd3c6b60dfb3553059c7b7bdcd8dc Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Wed, 30 Jan 2013 16:04:47 +0100 Subject: [PATCH 25/38] Added my two new compressor classes - one based on Miniz and the other based on RLE. --- library/PolyVoxCore/CMakeLists.txt | 4 + .../include/PolyVoxCore/AStarPathfinder.inl | 2 +- .../include/PolyVoxCore/MinizCompressor.h | 16 ++++ .../include/PolyVoxCore/RLECompressor.h | 24 ++++++ .../include/PolyVoxCore/RLECompressor.inl | 85 +++++++++++++++++++ .../polyvoxcore/source/MinizCompressor.cpp | 45 ++++++++++ 6 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 library/PolyVoxCore/include/PolyVoxCore/MinizCompressor.h create mode 100644 library/PolyVoxCore/include/PolyVoxCore/RLECompressor.h create mode 100644 library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl create mode 100644 library/polyvoxcore/source/MinizCompressor.cpp diff --git a/library/PolyVoxCore/CMakeLists.txt b/library/PolyVoxCore/CMakeLists.txt index 43f95a91..c62d34ff 100644 --- a/library/PolyVoxCore/CMakeLists.txt +++ b/library/PolyVoxCore/CMakeLists.txt @@ -31,6 +31,7 @@ endif() SET(CORE_SRC_FILES source/ArraySizes.cpp source/AStarPathfinder.cpp + source/MinizCompressor.cpp source/Log.cpp source/Region.cpp source/VertexTypes.cpp @@ -71,6 +72,7 @@ SET(CORE_INC_FILES include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl include/PolyVoxCore/Material.h include/PolyVoxCore/MaterialDensityPair.h + include/PolyVoxCore/MinizCompressor.h include/PolyVoxCore/PolyVoxForwardDeclarations.h include/PolyVoxCore/RawVolume.h include/PolyVoxCore/RawVolume.inl @@ -78,6 +80,8 @@ SET(CORE_INC_FILES include/PolyVoxCore/Raycast.h include/PolyVoxCore/Raycast.inl include/PolyVoxCore/Region.h + include/PolyVoxCore/RLECompressor.h + include/PolyVoxCore/RLECompressor.inl include/PolyVoxCore/SimpleVolume.h include/PolyVoxCore/SimpleVolume.inl include/PolyVoxCore/SimpleVolumeBlock.inl diff --git a/library/PolyVoxCore/include/PolyVoxCore/AStarPathfinder.inl b/library/PolyVoxCore/include/PolyVoxCore/AStarPathfinder.inl index 4df72be0..cf490afe 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/AStarPathfinder.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/AStarPathfinder.inl @@ -336,7 +336,7 @@ namespace PolyVox } // Robert Jenkins' 32 bit integer hash function - // http://www.concentric.net/~ttwang/tech/inthash.htm + // http://www.burtleburtle.net/bob/hash/integer.html template uint32_t AStarPathfinder::hash( uint32_t a) { diff --git a/library/PolyVoxCore/include/PolyVoxCore/MinizCompressor.h b/library/PolyVoxCore/include/PolyVoxCore/MinizCompressor.h new file mode 100644 index 00000000..51474097 --- /dev/null +++ b/library/PolyVoxCore/include/PolyVoxCore/MinizCompressor.h @@ -0,0 +1,16 @@ +#ifndef __PolyVox_MinizCompressor_H__ +#define __PolyVox_MinizCompressor_H__ + +#include "PolyVoxCore/Impl/TypeDef.h" + +class MinizCompressor +{ +public: + MinizCompressor(); + ~MinizCompressor(); + + uint32_t compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength); + uint32_t decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength); +}; + +#endif //__PolyVox_MinizCompressor_H__ diff --git a/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.h b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.h new file mode 100644 index 00000000..c2611eb2 --- /dev/null +++ b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.h @@ -0,0 +1,24 @@ +#ifndef __PolyVox_RLECompressor_H__ +#define __PolyVox_RLECompressor_H__ + +#include + +template +class RLECompressor +{ + struct Run + { + ValueType value; + LengthType length; + }; +public: + RLECompressor(); + ~RLECompressor(); + + uint32_t compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength); + uint32_t decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength); +}; + +#include "RLECompressor.inl" + +#endif //__PolyVox_RLECompressor_H__ diff --git a/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl new file mode 100644 index 00000000..8b897f6c --- /dev/null +++ b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl @@ -0,0 +1,85 @@ +#include +#include + +template +RLECompressor::RLECompressor() +{ +} + +template +RLECompressor::~RLECompressor() +{ +} + +template +uint32_t RLECompressor::compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) +{ + assert(uSrcLength % sizeof(ValueType) == 0); + + uSrcLength /= sizeof(ValueType); + uDstLength /= sizeof(Run); + + ValueType* pSrcDataAsInt = reinterpret_cast(pSrcData); + Run* pDstDataAsRun = reinterpret_cast(pDstData); + + ValueType* pSrcDataEnd = pSrcDataAsInt + uSrcLength; + Run* pDstDataEnd = pDstDataAsRun + uDstLength; + + uint32_t uDstLengthInBytes = 0; + + + pDstDataAsRun->value = *pSrcDataAsInt; + pSrcDataAsInt++; + pDstDataAsRun->length = 1; + uDstLengthInBytes += sizeof(Run); + + while(pSrcDataAsInt < pSrcDataEnd) + { + if((*pSrcDataAsInt == pDstDataAsRun->value) && (pDstDataAsRun->length < std::numeric_limits::max())) + { + pDstDataAsRun->length++; + } + else + { + pDstDataAsRun++; + assert(pDstDataAsRun < pDstDataEnd); + pDstDataAsRun->value = *pSrcDataAsInt; + pDstDataAsRun->length = 1; + uDstLengthInBytes += sizeof(Run); + } + + pSrcDataAsInt++; + } + + return uDstLengthInBytes; +} + +template +uint32_t RLECompressor::decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) +{ + assert(uSrcLength % sizeof(Run) == 0); + + uSrcLength /= sizeof(Run); + uDstLength /= sizeof(ValueType); + + Run* pSrcDataAsRun = reinterpret_cast(pSrcData); + ValueType* pDstDataAsInt = reinterpret_cast(pDstData); + + Run* pSrcDataEnd = pSrcDataAsRun + uSrcLength; + ValueType* pDstDataEnd = pDstDataAsInt + uDstLength; + + uint32_t uDstLengthInBytes = 0; + + while(pSrcDataAsRun < pSrcDataEnd) + { + std::fill(pDstDataAsInt, pDstDataAsInt + pSrcDataAsRun->length, pSrcDataAsRun->value); + pDstDataAsInt += pSrcDataAsRun->length; + + uDstLengthInBytes += pSrcDataAsRun->length * sizeof(ValueType); + + pSrcDataAsRun++; + } + + return uDstLengthInBytes; +} + diff --git a/library/polyvoxcore/source/MinizCompressor.cpp b/library/polyvoxcore/source/MinizCompressor.cpp new file mode 100644 index 00000000..7e4cbc3e --- /dev/null +++ b/library/polyvoxcore/source/MinizCompressor.cpp @@ -0,0 +1,45 @@ +#include "PolyVoxCore/MinizCompressor.h" + +// Diable things we don't need, and in particular the zlib compatible names which +// would cause conflicts if a user application is using both PolyVox and zlib. +#define MINIZ_NO_STDIO +#define MINIZ_NO_ARCHIVE_APIS +#define MINIZ_NO_TIME +//#define MINIZ_NO_ZLIB_APIS +#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES +//#define MINIZ_NO_MALLOC + +// For some unknown reason the miniz library is supplied only as a +// single .c file without a header. Apparently the only way to use +// it is then to #include it directly which is what the examples do. +#include "PolyVoxCore/Impl/miniz.c" + +MinizCompressor::MinizCompressor() +{ +} + +MinizCompressor::~MinizCompressor() +{ +} + +uint32_t MinizCompressor::compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) +{ + mz_ulong ulDstLength = uDstLength; + + // Do the compression + int result = mz_compress((unsigned char*)pDstData, &ulDstLength, (const unsigned char*) pSrcData, uSrcLength); + assert(result == MZ_OK); + + // Return the number of bytes written to the output. + return ulDstLength; +} + +uint32_t MinizCompressor::decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) +{ + mz_ulong ulDstLength = uDstLength; + + int result = mz_uncompress((unsigned char*) pDstData, &ulDstLength, (const unsigned char*) pSrcData, uSrcLength); + assert(result == MZ_OK); + + return ulDstLength; +} \ No newline at end of file From a1cdf78250f065845be7986bd637cb2bcf5facfa Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Wed, 30 Jan 2013 16:49:06 +0100 Subject: [PATCH 26/38] Block now working with new MinizCompressor. Removing some old compression code. --- library/PolyVoxCore/CMakeLists.txt | 2 - .../include/PolyVoxCore/Impl/Block.h | 2 +- .../include/PolyVoxCore/Impl/Block.inl | 35 +++++++++-- .../include/PolyVoxCore/Impl/Compression.h | 38 ----------- .../PolyVoxCore/source/Impl/Compression.cpp | 63 ------------------- 5 files changed, 30 insertions(+), 110 deletions(-) delete mode 100644 library/PolyVoxCore/include/PolyVoxCore/Impl/Compression.h delete mode 100644 library/PolyVoxCore/source/Impl/Compression.cpp diff --git a/library/PolyVoxCore/CMakeLists.txt b/library/PolyVoxCore/CMakeLists.txt index c62d34ff..41d8ad78 100644 --- a/library/PolyVoxCore/CMakeLists.txt +++ b/library/PolyVoxCore/CMakeLists.txt @@ -98,7 +98,6 @@ SET(CORE_INC_FILES ) SET(IMPL_SRC_FILES - source/Impl/Compression.cpp source/Impl/ErrorHandling.cpp source/Impl/MarchingCubesTables.cpp source/Impl/RandomUnitVectors.cpp @@ -113,7 +112,6 @@ SET(IMPL_INC_FILES include/PolyVoxCore/Impl/Block.h include/PolyVoxCore/Impl/Block.inl include/PolyVoxCore/Impl/CompilerCapabilities.h - include/PolyVoxCore/Impl/Compression.h include/PolyVoxCore/Impl/Config.h include/PolyVoxCore/Impl/ErrorHandling.h include/PolyVoxCore/Impl/MarchingCubesTables.h diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h index 3c253186..269f0079 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h @@ -64,7 +64,7 @@ namespace PolyVox void uncompress(void); std::vector< RunlengthEntry > m_vecCompressedData; - uint8_t* m_pCompressedData; + void* m_pCompressedData; uint32_t m_uCompressedDataLength; VoxelType* m_tUncompressedData; uint16_t m_uSideLength; diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl index 65325b65..8876e1b7 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl @@ -21,10 +21,10 @@ freely, subject to the following restrictions: distribution. *******************************************************************************/ -#include "PolyVoxCore/Impl/Compression.h" #include "PolyVoxCore/Impl/ErrorHandling.h" #include "PolyVoxCore/Impl/Utility.h" +#include "PolyVoxCore/MinizCompressor.h" #include "PolyVoxCore/Vector.h" #include "PolyVoxCore/Impl/ErrorHandling.h" @@ -151,14 +151,29 @@ namespace PolyVox //modified then we don't need to redo the compression. if(m_bIsUncompressedDataModified) { - Data src; + void* pSrcData = reinterpret_cast(m_tUncompressedData); + void* pDstData = reinterpret_cast( new uint8_t[1000000] ); + uint32_t uSrcLength = m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType); + uint32_t uDstLength = 1000000; + + MinizCompressor compressor; + uint32_t uCompressedLength = compressor.compress(pSrcData, uSrcLength, pDstData, uDstLength); + + m_pCompressedData = reinterpret_cast( new uint8_t[uCompressedLength] ); + memcpy(m_pCompressedData, pDstData, uCompressedLength); + m_uCompressedDataLength = uCompressedLength; + + delete pDstData; + + + /*Data src; src.ptr = reinterpret_cast(m_tUncompressedData); src.length = m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType); Data compressedResult = polyvox_compress(src); m_pCompressedData = compressedResult.ptr; - m_uCompressedDataLength = compressedResult.length; + m_uCompressedDataLength = compressedResult.length;*/ /*uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength; m_vecCompressedData.clear(); @@ -212,7 +227,15 @@ namespace PolyVox m_tUncompressedData = new VoxelType[m_uSideLength * m_uSideLength * m_uSideLength]; - Data src; + void* pSrcData = reinterpret_cast(m_pCompressedData); + void* pDstData = reinterpret_cast(m_tUncompressedData); + uint32_t uSrcLength = m_uCompressedDataLength; + uint32_t uDstLength = m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType); + + MinizCompressor compressor; + uint32_t uUncompressedLength = compressor.decompress(pSrcData, uSrcLength, pDstData, uDstLength); + + /*Data src; src.ptr = m_pCompressedData; src.length = m_uCompressedDataLength; @@ -220,9 +243,9 @@ namespace PolyVox dst.ptr = reinterpret_cast(m_tUncompressedData); dst.length = m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType); - polyvox_decompress(src, dst); + polyvox_decompress(src, dst);*/ - POLYVOX_ASSERT(dst.length == m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType), "Destination length has changed."); + POLYVOX_ASSERT(uUncompressedLength == m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType), "Destination length has changed."); //m_tUncompressedData = reinterpret_cast(uncompressedResult.ptr); diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Compression.h b/library/PolyVoxCore/include/PolyVoxCore/Impl/Compression.h deleted file mode 100644 index 6c98b4a0..00000000 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Compression.h +++ /dev/null @@ -1,38 +0,0 @@ -/******************************************************************************* -Copyright (c) 2005-2009 David Williams - -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. - -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: - - 1. 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. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*******************************************************************************/ - -#ifndef __PolyVox_Compression_H__ -#define __PolyVox_Compression_H__ - -#include "PolyVoxCore/Impl/TypeDef.h" - -struct Data -{ - uint8_t* ptr; - unsigned long length; // Think what type this should be. -}; - -Data polyvox_compress(Data src); -void polyvox_decompress(Data src, Data dst); - -#endif //__PolyVox_Compression_H__ \ No newline at end of file diff --git a/library/PolyVoxCore/source/Impl/Compression.cpp b/library/PolyVoxCore/source/Impl/Compression.cpp deleted file mode 100644 index d533b430..00000000 --- a/library/PolyVoxCore/source/Impl/Compression.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "PolyVoxCore/Impl/Compression.h" -#include "PolyVoxCore/Impl/ErrorHandling.h" - -// The miniz library is supplied only as a single .c file without a header. -// Apparently the only way to use it would then be to #include it directly -// which is what the examples do. -#include "PolyVoxCore/Impl/miniz.c" - -Data polyvox_compress(Data src) -{ - POLYVOX_ASSERT(src.ptr != 0, "Source data cannot be null"); - - unsigned char* buffer; - mz_ulong compressedDataLength = compressBound(src.length); - buffer = new unsigned char[compressedDataLength]; - - int iCompressionResult = compress(buffer, &compressedDataLength, static_cast(src.ptr), src.length); - - // Debug more checking - POLYVOX_ASSERT(iCompressionResult == Z_OK, "Data compression failed."); - - // Release mode checking - if (iCompressionResult != Z_OK) - { - delete[] buffer; - POLYVOX_THROW(std::runtime_error, "Data compression failed."); - } - - Data dst; - dst.length = compressedDataLength; - dst.ptr = new uint8_t[dst.length]; - - memcpy(dst.ptr, buffer, compressedDataLength); - - delete[] buffer; - - return dst; -} - -void polyvox_decompress(Data src, Data dst) -{ - /*unsigned char* buffer; - mz_ulong uncompressedDataLength = 1000000; - buffer = new unsigned char[uncompressedDataLength];*/ - - int iUncompressionResult = uncompress(dst.ptr, &dst.length, src.ptr, src.length); - - if (iUncompressionResult != Z_OK) - { - //delete[] buffer; - POLYVOX_THROW(std::runtime_error, "Data decompression failed."); - } - - /*Data dst; - dst.length = uncompressedDataLength; - dst.ptr = new uint8_t[dst.length]; - - memcpy(dst.ptr, buffer, uncompressedDataLength); - - delete[] buffer;*/ - - //return dst; -} \ No newline at end of file From 924744c5e633aaaf62d61bfd20cbea105b2ea230 Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Wed, 30 Jan 2013 16:58:13 +0100 Subject: [PATCH 27/38] Looks like RLECompressor works as well. --- .../include/PolyVoxCore/Impl/Block.inl | 64 ++----------------- .../include/PolyVoxCore/RLECompressor.h | 2 +- .../include/PolyVoxCore/RLECompressor.inl | 3 +- 3 files changed, 8 insertions(+), 61 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl index 8876e1b7..5cdeb34c 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl @@ -25,6 +25,7 @@ freely, subject to the following restrictions: #include "PolyVoxCore/Impl/Utility.h" #include "PolyVoxCore/MinizCompressor.h" +#include "PolyVoxCore/RLECompressor.h" #include "PolyVoxCore/Vector.h" #include "PolyVoxCore/Impl/ErrorHandling.h" @@ -156,7 +157,8 @@ namespace PolyVox uint32_t uSrcLength = m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType); uint32_t uDstLength = 1000000; - MinizCompressor compressor; + //MinizCompressor compressor; + RLECompressor compressor; uint32_t uCompressedLength = compressor.compress(pSrcData, uSrcLength, pDstData, uDstLength); m_pCompressedData = reinterpret_cast( new uint8_t[uCompressedLength] ); @@ -164,45 +166,6 @@ namespace PolyVox m_uCompressedDataLength = uCompressedLength; delete pDstData; - - - /*Data src; - src.ptr = reinterpret_cast(m_tUncompressedData); - src.length = m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType); - - Data compressedResult = polyvox_compress(src); - - m_pCompressedData = compressedResult.ptr; - m_uCompressedDataLength = compressedResult.length;*/ - - /*uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength; - m_vecCompressedData.clear(); - - RunlengthEntry entry; - entry.length = 1; - entry.value = m_tUncompressedData[0]; - - for(uint32_t ct = 1; ct < uNoOfVoxels; ++ct) - { - VoxelType value = m_tUncompressedData[ct]; - if((value == entry.value) && (entry.length < entry.maxRunlength())) - { - entry.length++; - } - else - { - m_vecCompressedData.push_back(entry); - entry.value = value; - entry.length = 1; - } - } - - m_vecCompressedData.push_back(entry); - - //Shrink the vectors to their contents (maybe slow?): - //http://stackoverflow.com/questions/1111078/reduce-the-capacity-of-an-stl-vector - //C++0x may have a shrink_to_fit() function? - std::vector< RunlengthEntry >(m_vecCompressedData).swap(m_vecCompressedData);*/ } //Flag the uncompressed data as no longer being used. @@ -216,14 +179,6 @@ namespace PolyVox { POLYVOX_ASSERT(m_bIsCompressed == true, "Attempted to uncompress block which is not flagged as compressed."); POLYVOX_ASSERT(m_tUncompressedData == 0, "Uncompressed data already exists."); - /*m_tUncompressedData = new VoxelType[m_uSideLength * m_uSideLength * m_uSideLength]; - - VoxelType* pUncompressedData = m_tUncompressedData; - for(uint32_t ct = 0; ct < m_vecCompressedData.size(); ++ct) - { - std::fill(pUncompressedData, pUncompressedData + m_vecCompressedData[ct].length, m_vecCompressedData[ct].value); - pUncompressedData += m_vecCompressedData[ct].length; - }*/ m_tUncompressedData = new VoxelType[m_uSideLength * m_uSideLength * m_uSideLength]; @@ -232,19 +187,10 @@ namespace PolyVox uint32_t uSrcLength = m_uCompressedDataLength; uint32_t uDstLength = m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType); - MinizCompressor compressor; + //MinizCompressor compressor; + RLECompressor compressor; uint32_t uUncompressedLength = compressor.decompress(pSrcData, uSrcLength, pDstData, uDstLength); - /*Data src; - src.ptr = m_pCompressedData; - src.length = m_uCompressedDataLength; - - Data dst; - dst.ptr = reinterpret_cast(m_tUncompressedData); - dst.length = m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType); - - polyvox_decompress(src, dst);*/ - POLYVOX_ASSERT(uUncompressedLength == m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType), "Destination length has changed."); //m_tUncompressedData = reinterpret_cast(uncompressedResult.ptr); diff --git a/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.h b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.h index c2611eb2..8540b5ac 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.h @@ -1,7 +1,7 @@ #ifndef __PolyVox_RLECompressor_H__ #define __PolyVox_RLECompressor_H__ -#include +#include "PolyVoxCore/Impl/TypeDef.h" template class RLECompressor diff --git a/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl index 8b897f6c..167206c6 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl @@ -1,5 +1,6 @@ #include #include +#include template RLECompressor::RLECompressor() @@ -35,7 +36,7 @@ uint32_t RLECompressor::compress(void* pSrcData, uint32_t while(pSrcDataAsInt < pSrcDataEnd) { - if((*pSrcDataAsInt == pDstDataAsRun->value) && (pDstDataAsRun->length < std::numeric_limits::max())) + if((*pSrcDataAsInt == pDstDataAsRun->value) && (pDstDataAsRun->length < (std::numeric_limits::max)())) { pDstDataAsRun->length++; } From 36676433bef9c595348e029b39d8267ef5b73dad Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Thu, 31 Jan 2013 15:54:04 +0100 Subject: [PATCH 28/38] Work on compression interface. --- library/PolyVoxCore/CMakeLists.txt | 1 + .../include/PolyVoxCore/Compressor.h | 42 +++++ .../include/PolyVoxCore/Impl/Block.h | 7 +- .../include/PolyVoxCore/Impl/Block.inl | 10 +- .../include/PolyVoxCore/LargeVolume.h | 5 + .../include/PolyVoxCore/LargeVolume.inl | 10 +- .../include/PolyVoxCore/MinizCompressor.h | 19 ++- .../include/PolyVoxCore/RLECompressor.h | 29 ++-- .../include/PolyVoxCore/RLECompressor.inl | 148 +++++++++--------- .../polyvoxcore/source/MinizCompressor.cpp | 57 +++---- 10 files changed, 196 insertions(+), 132 deletions(-) create mode 100644 library/PolyVoxCore/include/PolyVoxCore/Compressor.h diff --git a/library/PolyVoxCore/CMakeLists.txt b/library/PolyVoxCore/CMakeLists.txt index 41d8ad78..65492a6f 100644 --- a/library/PolyVoxCore/CMakeLists.txt +++ b/library/PolyVoxCore/CMakeLists.txt @@ -49,6 +49,7 @@ SET(CORE_INC_FILES include/PolyVoxCore/BaseVolume.h include/PolyVoxCore/BaseVolume.inl include/PolyVoxCore/BaseVolumeSampler.inl + include/PolyVoxCore/Compressor.h include/PolyVoxCore/ConstVolumeProxy.h include/PolyVoxCore/CubicSurfaceExtractor.h include/PolyVoxCore/CubicSurfaceExtractor.inl diff --git a/library/PolyVoxCore/include/PolyVoxCore/Compressor.h b/library/PolyVoxCore/include/PolyVoxCore/Compressor.h new file mode 100644 index 00000000..2d79d99b --- /dev/null +++ b/library/PolyVoxCore/include/PolyVoxCore/Compressor.h @@ -0,0 +1,42 @@ +/******************************************************************************* +Copyright (c) 2005-2009 David Williams + +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. + +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: + + 1. 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. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*******************************************************************************/ + +#ifndef __PolyVox_Compressor_H__ +#define __PolyVox_Compressor_H__ + +#include "PolyVoxCore/Impl/TypeDef.h" + +namespace PolyVox +{ + class Compressor + { + public: + Compressor() {}; + virtual ~Compressor() {}; + + virtual uint32_t compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) = 0; + virtual uint32_t decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) = 0; + }; +} + +#endif //__PolyVox_Compressor_H__ diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h index 269f0079..ea5541e6 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h @@ -25,6 +25,8 @@ freely, subject to the following restrictions: #define __PolyVox_Block_H__ #include "PolyVoxCore/Impl/TypeDef.h" + +#include "PolyVoxCore/Compressor.h" #include "PolyVoxCore/Vector.h" #include @@ -60,10 +62,9 @@ namespace PolyVox uint32_t calculateSizeInBytes(void); public: - void compress(void); - void uncompress(void); + void compress(Compressor* pCompressor); + void uncompress(Compressor* pCompressor); - std::vector< RunlengthEntry > m_vecCompressedData; void* m_pCompressedData; uint32_t m_uCompressedDataLength; VoxelType* m_tUncompressedData; diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl index 5cdeb34c..3f0829dd 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl @@ -131,20 +131,21 @@ namespace PolyVox m_bIsUncompressedDataModified = true; //For some reason blocks start out compressed. We should probably change this. - compress(); + compress(0); } template uint32_t Block::calculateSizeInBytes(void) { + //FIXME - This function is incomplete. uint32_t uSizeInBytes = sizeof(Block); - uSizeInBytes += m_vecCompressedData.capacity() * sizeof(RunlengthEntry); return uSizeInBytes; } template - void Block::compress(void) + void Block::compress(Compressor* pCompressor) { + //POLYVOX_ASSERT(pCompressor, "Compressor is not valid"); POLYVOX_ASSERT(m_bIsCompressed == false, "Attempted to compress block which is already flagged as compressed."); POLYVOX_ASSERT(m_tUncompressedData != 0, "No uncompressed data is present."); @@ -175,8 +176,9 @@ namespace PolyVox } template - void Block::uncompress(void) + void Block::uncompress(Compressor* pCompressor) { + //POLYVOX_ASSERT(pCompressor, "Compressor is not valid"); POLYVOX_ASSERT(m_bIsCompressed == true, "Attempted to uncompress block which is not flagged as compressed."); POLYVOX_ASSERT(m_tUncompressedData == 0, "Uncompressed data already exists."); diff --git a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h index b35f0cae..5cae0359 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h +++ b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h @@ -26,6 +26,7 @@ freely, subject to the following restrictions: #include "PolyVoxCore/BaseVolume.h" #include "Impl/Block.h" +#include "PolyVoxCore/Compressor.h" #include "PolyVoxCore/Log.h" #include "PolyVoxCore/Region.h" #include "PolyVoxCore/Vector.h" @@ -256,6 +257,7 @@ namespace PolyVox LargeVolume ( const Region& regValid, + Compressor* pCompressor = 0, polyvox_function&, const Region&)> dataRequiredHandler = 0, polyvox_function&, const Region&)> dataOverflowHandler = 0, bool bPagingEnabled = false, @@ -363,6 +365,9 @@ namespace PolyVox uint16_t m_uBlockSideLength; uint8_t m_uBlockSideLengthPower; + //The compressor used by the Blocks to compress their data if required. + Compressor* m_pCompressor; + bool m_bCompressionEnabled; bool m_bPagingEnabled; }; diff --git a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl index 5cc06a9a..a048d03c 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl @@ -62,12 +62,14 @@ namespace PolyVox LargeVolume::LargeVolume ( const Region& regValid, + Compressor* pCompressor, polyvox_function&, const Region&)> dataRequiredHandler, polyvox_function&, const Region&)> dataOverflowHandler, bool bPagingEnabled, uint16_t uBlockSideLength ) :BaseVolume(regValid) + ,m_pCompressor(pCompressor) { m_funcDataRequiredHandler = dataRequiredHandler; m_funcDataOverflowHandler = dataOverflowHandler; @@ -456,7 +458,7 @@ namespace PolyVox { for(uint32_t ct = 0; ct < m_vecUncompressedBlockCache.size(); ct++) { - m_vecUncompressedBlockCache[ct]->block.compress(); + m_vecUncompressedBlockCache[ct]->block.compress(m_pCompressor); } m_vecUncompressedBlockCache.clear(); } @@ -537,7 +539,7 @@ namespace PolyVox if(m_vecUncompressedBlockCache[ct] == &(itBlock->second)) { // TODO: compression is unneccessary? or will not compressing this cause a memleak? - itBlock->second.block.compress(); + itBlock->second.block.compress(m_pCompressor); // put last object in cache here m_vecUncompressedBlockCache[ct] = m_vecUncompressedBlockCache.back(); // decrease cache size by one since last element is now in here twice @@ -667,7 +669,7 @@ namespace PolyVox } //Compress the least recently used block. - m_vecUncompressedBlockCache[leastRecentlyUsedBlockIndex]->block.compress(); + m_vecUncompressedBlockCache[leastRecentlyUsedBlockIndex]->block.compress(m_pCompressor); //We don't actually remove any elements from this vector, we //simply change the pointer to point at the new uncompressed bloack. @@ -678,7 +680,7 @@ namespace PolyVox m_vecUncompressedBlockCache.push_back(&loadedBlock); } - loadedBlock.block.uncompress(); + loadedBlock.block.uncompress(m_pCompressor); m_pLastAccessedBlock = &(loadedBlock.block); POLYVOX_ASSERT(m_pLastAccessedBlock->m_tUncompressedData, "Block has no uncompressed data"); diff --git a/library/PolyVoxCore/include/PolyVoxCore/MinizCompressor.h b/library/PolyVoxCore/include/PolyVoxCore/MinizCompressor.h index 51474097..3c4ba64d 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MinizCompressor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MinizCompressor.h @@ -1,16 +1,19 @@ #ifndef __PolyVox_MinizCompressor_H__ #define __PolyVox_MinizCompressor_H__ -#include "PolyVoxCore/Impl/TypeDef.h" +#include "PolyVoxCore/Compressor.h" -class MinizCompressor +namespace PolyVox { -public: - MinizCompressor(); - ~MinizCompressor(); + class MinizCompressor : public Compressor + { + public: + MinizCompressor(); + ~MinizCompressor(); - uint32_t compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength); - uint32_t decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength); -}; + uint32_t compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength); + uint32_t decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength); + }; +} #endif //__PolyVox_MinizCompressor_H__ diff --git a/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.h b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.h index 8540b5ac..f17cdf51 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.h @@ -1,23 +1,26 @@ #ifndef __PolyVox_RLECompressor_H__ #define __PolyVox_RLECompressor_H__ -#include "PolyVoxCore/Impl/TypeDef.h" +#include "PolyVoxCore/Compressor.h" -template -class RLECompressor +namespace PolyVox { - struct Run + template + class RLECompressor : public Compressor { - ValueType value; - LengthType length; - }; -public: - RLECompressor(); - ~RLECompressor(); + struct Run + { + ValueType value; + LengthType length; + }; + public: + RLECompressor(); + ~RLECompressor(); - uint32_t compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength); - uint32_t decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength); -}; + uint32_t compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength); + uint32_t decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength); + }; +} #include "RLECompressor.inl" diff --git a/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl index 167206c6..600e2cf9 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl @@ -2,85 +2,87 @@ #include #include -template -RLECompressor::RLECompressor() +namespace PolyVox { -} - -template -RLECompressor::~RLECompressor() -{ -} - -template -uint32_t RLECompressor::compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) -{ - assert(uSrcLength % sizeof(ValueType) == 0); - - uSrcLength /= sizeof(ValueType); - uDstLength /= sizeof(Run); - - ValueType* pSrcDataAsInt = reinterpret_cast(pSrcData); - Run* pDstDataAsRun = reinterpret_cast(pDstData); - - ValueType* pSrcDataEnd = pSrcDataAsInt + uSrcLength; - Run* pDstDataEnd = pDstDataAsRun + uDstLength; - - uint32_t uDstLengthInBytes = 0; - - - pDstDataAsRun->value = *pSrcDataAsInt; - pSrcDataAsInt++; - pDstDataAsRun->length = 1; - uDstLengthInBytes += sizeof(Run); - - while(pSrcDataAsInt < pSrcDataEnd) + template + RLECompressor::RLECompressor() { - if((*pSrcDataAsInt == pDstDataAsRun->value) && (pDstDataAsRun->length < (std::numeric_limits::max)())) - { - pDstDataAsRun->length++; - } - else - { - pDstDataAsRun++; - assert(pDstDataAsRun < pDstDataEnd); - pDstDataAsRun->value = *pSrcDataAsInt; - pDstDataAsRun->length = 1; - uDstLengthInBytes += sizeof(Run); - } - + } + + template + RLECompressor::~RLECompressor() + { + } + + template + uint32_t RLECompressor::compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) + { + assert(uSrcLength % sizeof(ValueType) == 0); + + uSrcLength /= sizeof(ValueType); + uDstLength /= sizeof(Run); + + ValueType* pSrcDataAsInt = reinterpret_cast(pSrcData); + Run* pDstDataAsRun = reinterpret_cast(pDstData); + + ValueType* pSrcDataEnd = pSrcDataAsInt + uSrcLength; + Run* pDstDataEnd = pDstDataAsRun + uDstLength; + + uint32_t uDstLengthInBytes = 0; + + + pDstDataAsRun->value = *pSrcDataAsInt; pSrcDataAsInt++; + pDstDataAsRun->length = 1; + uDstLengthInBytes += sizeof(Run); + + while(pSrcDataAsInt < pSrcDataEnd) + { + if((*pSrcDataAsInt == pDstDataAsRun->value) && (pDstDataAsRun->length < (std::numeric_limits::max)())) + { + pDstDataAsRun->length++; + } + else + { + pDstDataAsRun++; + assert(pDstDataAsRun < pDstDataEnd); + pDstDataAsRun->value = *pSrcDataAsInt; + pDstDataAsRun->length = 1; + uDstLengthInBytes += sizeof(Run); + } + + pSrcDataAsInt++; + } + + return uDstLengthInBytes; } - return uDstLengthInBytes; -} - -template -uint32_t RLECompressor::decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) -{ - assert(uSrcLength % sizeof(Run) == 0); - - uSrcLength /= sizeof(Run); - uDstLength /= sizeof(ValueType); - - Run* pSrcDataAsRun = reinterpret_cast(pSrcData); - ValueType* pDstDataAsInt = reinterpret_cast(pDstData); - - Run* pSrcDataEnd = pSrcDataAsRun + uSrcLength; - ValueType* pDstDataEnd = pDstDataAsInt + uDstLength; - - uint32_t uDstLengthInBytes = 0; - - while(pSrcDataAsRun < pSrcDataEnd) + template + uint32_t RLECompressor::decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) { - std::fill(pDstDataAsInt, pDstDataAsInt + pSrcDataAsRun->length, pSrcDataAsRun->value); - pDstDataAsInt += pSrcDataAsRun->length; + assert(uSrcLength % sizeof(Run) == 0); - uDstLengthInBytes += pSrcDataAsRun->length * sizeof(ValueType); - - pSrcDataAsRun++; + uSrcLength /= sizeof(Run); + uDstLength /= sizeof(ValueType); + + Run* pSrcDataAsRun = reinterpret_cast(pSrcData); + ValueType* pDstDataAsInt = reinterpret_cast(pDstData); + + Run* pSrcDataEnd = pSrcDataAsRun + uSrcLength; + ValueType* pDstDataEnd = pDstDataAsInt + uDstLength; + + uint32_t uDstLengthInBytes = 0; + + while(pSrcDataAsRun < pSrcDataEnd) + { + std::fill(pDstDataAsInt, pDstDataAsInt + pSrcDataAsRun->length, pSrcDataAsRun->value); + pDstDataAsInt += pSrcDataAsRun->length; + + uDstLengthInBytes += pSrcDataAsRun->length * sizeof(ValueType); + + pSrcDataAsRun++; + } + + return uDstLengthInBytes; } - - return uDstLengthInBytes; } - diff --git a/library/polyvoxcore/source/MinizCompressor.cpp b/library/polyvoxcore/source/MinizCompressor.cpp index 7e4cbc3e..f0dc06a6 100644 --- a/library/polyvoxcore/source/MinizCompressor.cpp +++ b/library/polyvoxcore/source/MinizCompressor.cpp @@ -14,32 +14,35 @@ // it is then to #include it directly which is what the examples do. #include "PolyVoxCore/Impl/miniz.c" -MinizCompressor::MinizCompressor() +namespace PolyVox { + MinizCompressor::MinizCompressor() + { + } + + MinizCompressor::~MinizCompressor() + { + } + + uint32_t MinizCompressor::compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) + { + mz_ulong ulDstLength = uDstLength; + + // Do the compression + int result = mz_compress((unsigned char*)pDstData, &ulDstLength, (const unsigned char*) pSrcData, uSrcLength); + assert(result == MZ_OK); + + // Return the number of bytes written to the output. + return ulDstLength; + } + + uint32_t MinizCompressor::decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) + { + mz_ulong ulDstLength = uDstLength; + + int result = mz_uncompress((unsigned char*) pDstData, &ulDstLength, (const unsigned char*) pSrcData, uSrcLength); + assert(result == MZ_OK); + + return ulDstLength; + } } - -MinizCompressor::~MinizCompressor() -{ -} - -uint32_t MinizCompressor::compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) -{ - mz_ulong ulDstLength = uDstLength; - - // Do the compression - int result = mz_compress((unsigned char*)pDstData, &ulDstLength, (const unsigned char*) pSrcData, uSrcLength); - assert(result == MZ_OK); - - // Return the number of bytes written to the output. - return ulDstLength; -} - -uint32_t MinizCompressor::decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) -{ - mz_ulong ulDstLength = uDstLength; - - int result = mz_uncompress((unsigned char*) pDstData, &ulDstLength, (const unsigned char*) pSrcData, uSrcLength); - assert(result == MZ_OK); - - return ulDstLength; -} \ No newline at end of file From a81ec68714a3dcd9e950077763026810341184ed Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Thu, 31 Jan 2013 16:35:50 +0100 Subject: [PATCH 29/38] Work on compression interface. --- .../PolyVoxCore/include/PolyVoxCore/Impl/Block.inl | 13 +++++-------- .../PolyVoxCore/include/PolyVoxCore/LargeVolume.inl | 4 ++++ .../PolyVoxCore/PolyVoxForwardDeclarations.h | 5 +++++ tests/testvolume.cpp | 7 ++++++- tests/testvolume.h | 1 + 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl index 3f0829dd..b4abf7d6 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl @@ -129,9 +129,6 @@ namespace PolyVox const uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength; std::fill(m_tUncompressedData, m_tUncompressedData + uNoOfVoxels, VoxelType()); m_bIsUncompressedDataModified = true; - - //For some reason blocks start out compressed. We should probably change this. - compress(0); } template @@ -159,8 +156,8 @@ namespace PolyVox uint32_t uDstLength = 1000000; //MinizCompressor compressor; - RLECompressor compressor; - uint32_t uCompressedLength = compressor.compress(pSrcData, uSrcLength, pDstData, uDstLength); + //RLECompressor compressor; + uint32_t uCompressedLength = pCompressor->compress(pSrcData, uSrcLength, pDstData, uDstLength); m_pCompressedData = reinterpret_cast( new uint8_t[uCompressedLength] ); memcpy(m_pCompressedData, pDstData, uCompressedLength); @@ -178,7 +175,7 @@ namespace PolyVox template void Block::uncompress(Compressor* pCompressor) { - //POLYVOX_ASSERT(pCompressor, "Compressor is not valid"); + POLYVOX_ASSERT(pCompressor, "Compressor is not valid"); POLYVOX_ASSERT(m_bIsCompressed == true, "Attempted to uncompress block which is not flagged as compressed."); POLYVOX_ASSERT(m_tUncompressedData == 0, "Uncompressed data already exists."); @@ -190,8 +187,8 @@ namespace PolyVox uint32_t uDstLength = m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType); //MinizCompressor compressor; - RLECompressor compressor; - uint32_t uUncompressedLength = compressor.decompress(pSrcData, uSrcLength, pDstData, uDstLength); + //RLECompressor compressor; + uint32_t uUncompressedLength = pCompressor->decompress(pSrcData, uSrcLength, pDstData, uDstLength); POLYVOX_ASSERT(uUncompressedLength == m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType), "Destination length has changed."); diff --git a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl index a048d03c..c3f53407 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl @@ -618,6 +618,10 @@ namespace PolyVox // create the new block LoadedBlock newBlock(m_uBlockSideLength); + + //Blocks start out compressed - should we change this? + newBlock.block.compress(m_pCompressor); + itBlock = m_pBlocks.insert(std::make_pair(v3dBlockPos, newBlock)).first; //We have created the new block. If paging is enabled it should be used to diff --git a/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h b/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h index addaa962..d618bb6e 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h +++ b/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h @@ -60,6 +60,11 @@ namespace PolyVox typedef Array<3,int32_t> Array3DInt32; typedef Array<3,uint32_t> Array3DUint32; + //////////////////////////////////////////////////////////////////////////////// + // Compressor + //////////////////////////////////////////////////////////////////////////////// + class Compressor; + //////////////////////////////////////////////////////////////////////////////// // CubicSurfaceExtractor //////////////////////////////////////////////////////////////////////////////// diff --git a/tests/testvolume.cpp b/tests/testvolume.cpp index cdb9fbf5..5d704760 100644 --- a/tests/testvolume.cpp +++ b/tests/testvolume.cpp @@ -25,6 +25,7 @@ freely, subject to the following restrictions: #include "PolyVoxCore/LargeVolume.h" #include "PolyVoxCore/RawVolume.h" +#include "PolyVoxCore/RLECompressor.h" #include "PolyVoxCore/SimpleVolume.h" #include @@ -269,10 +270,12 @@ TestVolume::TestVolume() { Region region(-57, -31, 12, 64, 96, 131); // Deliberatly awkward size + m_pCompressor = new RLECompressor; + //Create the volumes m_pRawVolume = new RawVolume(region); m_pSimpleVolume = new SimpleVolume(region); - m_pLargeVolume = new LargeVolume(region); + m_pLargeVolume = new LargeVolume(region, m_pCompressor); // LargeVolume currently fails a test if compression is enabled. It // may be related to accessing the data through more than one sampler? @@ -299,6 +302,8 @@ TestVolume::~TestVolume() delete m_pRawVolume; delete m_pSimpleVolume; delete m_pLargeVolume; + + delete m_pCompressor; } /* diff --git a/tests/testvolume.h b/tests/testvolume.h index 7917f577..59a63ec8 100644 --- a/tests/testvolume.h +++ b/tests/testvolume.h @@ -65,6 +65,7 @@ private slots: void testLargeVolumeSamplersWithExternalBackwards(); private: + PolyVox::Compressor* m_pCompressor; PolyVox::RawVolume* m_pRawVolume; PolyVox::SimpleVolume* m_pSimpleVolume; PolyVox::LargeVolume* m_pLargeVolume; From 46e38c47145bd1acaf32fefa97647e1715eb868a Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Thu, 31 Jan 2013 16:56:32 +0100 Subject: [PATCH 30/38] Work on compression. --- .../include/PolyVoxCore/Impl/Block.h | 2 +- .../include/PolyVoxCore/Impl/Block.inl | 3 +-- .../include/PolyVoxCore/RLECompressor.inl | 10 ++++++++-- .../polyvoxcore/source/MinizCompressor.cpp | 19 +++++++++++++++++-- tests/testvolume.cpp | 4 +++- 5 files changed, 30 insertions(+), 8 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h index ea5541e6..e27b5b09 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.h @@ -26,7 +26,7 @@ freely, subject to the following restrictions: #include "PolyVoxCore/Impl/TypeDef.h" -#include "PolyVoxCore/Compressor.h" +#include "PolyVoxCore/PolyVoxForwardDeclarations.h" #include "PolyVoxCore/Vector.h" #include diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl index b4abf7d6..374ae124 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl @@ -24,8 +24,7 @@ freely, subject to the following restrictions: #include "PolyVoxCore/Impl/ErrorHandling.h" #include "PolyVoxCore/Impl/Utility.h" -#include "PolyVoxCore/MinizCompressor.h" -#include "PolyVoxCore/RLECompressor.h" +#include "PolyVoxCore/Compressor.h" #include "PolyVoxCore/Vector.h" #include "PolyVoxCore/Impl/ErrorHandling.h" diff --git a/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl index 600e2cf9..1b274d30 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl @@ -17,7 +17,10 @@ namespace PolyVox template uint32_t RLECompressor::compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) { - assert(uSrcLength % sizeof(ValueType) == 0); + if(uSrcLength % sizeof(ValueType) != 0) + { + POLYVOX_THROW(std::length_error, "Source length must be a integer multiple of the ValueType size"); + } uSrcLength /= sizeof(ValueType); uDstLength /= sizeof(Run); @@ -60,7 +63,10 @@ namespace PolyVox template uint32_t RLECompressor::decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) { - assert(uSrcLength % sizeof(Run) == 0); + if(uSrcLength % sizeof(Run) != 0) + { + POLYVOX_THROW(std::length_error, "Source length must be a integer multiple of the Run size"); + } uSrcLength /= sizeof(Run); uDstLength /= sizeof(ValueType); diff --git a/library/polyvoxcore/source/MinizCompressor.cpp b/library/polyvoxcore/source/MinizCompressor.cpp index f0dc06a6..30b7988c 100644 --- a/library/polyvoxcore/source/MinizCompressor.cpp +++ b/library/polyvoxcore/source/MinizCompressor.cpp @@ -9,11 +9,16 @@ #define MINIZ_NO_ZLIB_COMPATIBLE_NAMES //#define MINIZ_NO_MALLOC +#include "PolyVoxCore/Impl/ErrorHandling.h" // For some unknown reason the miniz library is supplied only as a // single .c file without a header. Apparently the only way to use // it is then to #include it directly which is what the examples do. #include "PolyVoxCore/Impl/miniz.c" +#include + +using namespace std; + namespace PolyVox { MinizCompressor::MinizCompressor() @@ -30,7 +35,12 @@ namespace PolyVox // Do the compression int result = mz_compress((unsigned char*)pDstData, &ulDstLength, (const unsigned char*) pSrcData, uSrcLength); - assert(result == MZ_OK); + if(result != MZ_OK) + { + stringstream ss; + ss << "mz_compress() failed with return code '" << result << "'"; + POLYVOX_THROW(std::runtime_error, ss.str()); + } // Return the number of bytes written to the output. return ulDstLength; @@ -41,7 +51,12 @@ namespace PolyVox mz_ulong ulDstLength = uDstLength; int result = mz_uncompress((unsigned char*) pDstData, &ulDstLength, (const unsigned char*) pSrcData, uSrcLength); - assert(result == MZ_OK); + if(result != MZ_OK) + { + stringstream ss; + ss << "mz_uncompress() failed with return code '" << result << "'"; + POLYVOX_THROW(std::runtime_error, ss.str()); + } return ulDstLength; } diff --git a/tests/testvolume.cpp b/tests/testvolume.cpp index 5d704760..74a3ffa1 100644 --- a/tests/testvolume.cpp +++ b/tests/testvolume.cpp @@ -24,6 +24,7 @@ freely, subject to the following restrictions: #include "testvolume.h" #include "PolyVoxCore/LargeVolume.h" +#include "PolyVoxCore/MinizCompressor.h" #include "PolyVoxCore/RawVolume.h" #include "PolyVoxCore/RLECompressor.h" #include "PolyVoxCore/SimpleVolume.h" @@ -270,7 +271,8 @@ TestVolume::TestVolume() { Region region(-57, -31, 12, 64, 96, 131); // Deliberatly awkward size - m_pCompressor = new RLECompressor; + //m_pCompressor = new RLECompressor; + m_pCompressor = new MinizCompressor; //Create the volumes m_pRawVolume = new RawVolume(region); From 3d66db50e0c655c7c92d999558f348994679cae3 Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Fri, 1 Feb 2013 14:09:29 +0100 Subject: [PATCH 31/38] Fixes and comments for RLE compression. --- .../include/PolyVoxCore/RLECompressor.inl | 57 +++++++++++++------ tests/testvolume.cpp | 4 +- 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl index 1b274d30..6a229d9d 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl @@ -22,39 +22,54 @@ namespace PolyVox POLYVOX_THROW(std::length_error, "Source length must be a integer multiple of the ValueType size"); } + // Lengths provided are in bytes, so convert them to be in terms of our types. uSrcLength /= sizeof(ValueType); uDstLength /= sizeof(Run); - ValueType* pSrcDataAsInt = reinterpret_cast(pSrcData); + // Get data pointers in the appropriate type + ValueType* pSrcDataAsType = reinterpret_cast(pSrcData); Run* pDstDataAsRun = reinterpret_cast(pDstData); - ValueType* pSrcDataEnd = pSrcDataAsInt + uSrcLength; + // Pointers to just past the end of the data + ValueType* pSrcDataEnd = pSrcDataAsType + uSrcLength; Run* pDstDataEnd = pDstDataAsRun + uDstLength; + //Counter for the output length uint32_t uDstLengthInBytes = 0; - - pDstDataAsRun->value = *pSrcDataAsInt; - pSrcDataAsInt++; + // Read the first element of the source and set up the first run based on it. + pDstDataAsRun->value = *pSrcDataAsType; + pSrcDataAsType++; pDstDataAsRun->length = 1; uDstLengthInBytes += sizeof(Run); - while(pSrcDataAsInt < pSrcDataEnd) + //Now process all remaining elements of the source. + while(pSrcDataAsType < pSrcDataEnd) { - if((*pSrcDataAsInt == pDstDataAsRun->value) && (pDstDataAsRun->length < (std::numeric_limits::max)())) + // If the value is the same as the current run (and we have not + // reached the maximum run length) then extend the current run. + if((*pSrcDataAsType == pDstDataAsRun->value) && (pDstDataAsRun->length < (std::numeric_limits::max)())) { pDstDataAsRun->length++; } + // Otherwise we need to start a new Run. else { pDstDataAsRun++; - assert(pDstDataAsRun < pDstDataEnd); - pDstDataAsRun->value = *pSrcDataAsInt; + + // Check if we have enough space in the destination buffer. + if(pDstDataAsRun >= pDstDataEnd) + { + POLYVOX_THROW(std::runtime_error, "Insufficient space in destination buffer."); + } + + // Create the new run. + pDstDataAsRun->value = *pSrcDataAsType; pDstDataAsRun->length = 1; uDstLengthInBytes += sizeof(Run); } - pSrcDataAsInt++; + pSrcDataAsType++; } return uDstLengthInBytes; @@ -68,24 +83,34 @@ namespace PolyVox POLYVOX_THROW(std::length_error, "Source length must be a integer multiple of the Run size"); } + // Lengths provided are in bytes, so convert them to be in terms of our types. uSrcLength /= sizeof(Run); uDstLength /= sizeof(ValueType); + // Get data pointers in the appropriate type Run* pSrcDataAsRun = reinterpret_cast(pSrcData); - ValueType* pDstDataAsInt = reinterpret_cast(pDstData); + ValueType* pDstDataAsType = reinterpret_cast(pDstData); + // Pointers to just past the end of the data Run* pSrcDataEnd = pSrcDataAsRun + uSrcLength; - ValueType* pDstDataEnd = pDstDataAsInt + uDstLength; + ValueType* pDstDataEnd = pDstDataAsType + uDstLength; + //Counter for the output length uint32_t uDstLengthInBytes = 0; while(pSrcDataAsRun < pSrcDataEnd) { - std::fill(pDstDataAsInt, pDstDataAsInt + pSrcDataAsRun->length, pSrcDataAsRun->value); - pDstDataAsInt += pSrcDataAsRun->length; + // Check if we have enough space in the destination buffer. + if(pDstDataAsType + pSrcDataAsRun->length > pDstDataEnd) + { + POLYVOX_THROW(std::runtime_error, "Insufficient space in destination buffer."); + } - uDstLengthInBytes += pSrcDataAsRun->length * sizeof(ValueType); - + // Write the run into the destination + std::fill(pDstDataAsType, pDstDataAsType + pSrcDataAsRun->length, pSrcDataAsRun->value); + pDstDataAsType += pSrcDataAsRun->length; + + uDstLengthInBytes += pSrcDataAsRun->length * sizeof(ValueType); pSrcDataAsRun++; } diff --git a/tests/testvolume.cpp b/tests/testvolume.cpp index 74a3ffa1..bb9c784c 100644 --- a/tests/testvolume.cpp +++ b/tests/testvolume.cpp @@ -271,8 +271,8 @@ TestVolume::TestVolume() { Region region(-57, -31, 12, 64, 96, 131); // Deliberatly awkward size - //m_pCompressor = new RLECompressor; - m_pCompressor = new MinizCompressor; + m_pCompressor = new RLECompressor; + //m_pCompressor = new MinizCompressor; //Create the volumes m_pRawVolume = new RawVolume(region); From a5b768e5f95baf887e4a6fc13c8ea6318ea74d74 Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Fri, 1 Feb 2013 15:43:20 +0100 Subject: [PATCH 32/38] Working to make compression handing more robust. --- .../include/PolyVoxCore/Compressor.h | 7 +++ .../include/PolyVoxCore/Impl/Block.inl | 47 ++++++++++++++++--- .../include/PolyVoxCore/LargeVolume.inl | 4 +- .../include/PolyVoxCore/MinizCompressor.h | 1 + .../include/PolyVoxCore/RLECompressor.h | 1 + .../include/PolyVoxCore/RLECompressor.inl | 7 +++ .../polyvoxcore/source/MinizCompressor.cpp | 5 ++ 7 files changed, 64 insertions(+), 8 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/Compressor.h b/library/PolyVoxCore/include/PolyVoxCore/Compressor.h index 2d79d99b..f731bd68 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Compressor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Compressor.h @@ -34,7 +34,14 @@ namespace PolyVox Compressor() {}; virtual ~Compressor() {}; + // Computes a worst-case scenario for how big the output can be for a given input size. If + // necessary you can use this as a destination buffer size, though it may be somewhat wasteful. + virtual uint32_t getMaxCompressedSize(uint32_t uUncompressedInputSize) = 0; + + // Compresses the data. virtual uint32_t compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) = 0; + + // Decompresses the data. virtual uint32_t decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) = 0; }; } diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl index 374ae124..ed893e47 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl @@ -150,19 +150,52 @@ namespace PolyVox if(m_bIsUncompressedDataModified) { void* pSrcData = reinterpret_cast(m_tUncompressedData); - void* pDstData = reinterpret_cast( new uint8_t[1000000] ); uint32_t uSrcLength = m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType); - uint32_t uDstLength = 1000000; - //MinizCompressor compressor; - //RLECompressor compressor; - uint32_t uCompressedLength = pCompressor->compress(pSrcData, uSrcLength, pDstData, uDstLength); + void* pDstData = 0; + uint32_t uCompressedLength = 0; + try + { + uint8_t buffer[1000000]; + + pDstData = reinterpret_cast( buffer ); + uint32_t uDstLength = 1000000; + + uCompressedLength = pCompressor->compress(pSrcData, uSrcLength, pDstData, uDstLength); + } + catch(std::exception& e) + { + // It is possible for the compression to fail. A common cause for this would be if the destination + // buffer is not big enough. So now we try again using a buffer that is definitely big enough. + uint32_t uMaxCompressedSize = pCompressor->getMaxCompressedSize(uSrcLength); + uint8_t* buffer = new uint8_t[ uMaxCompressedSize ]; + + pDstData = reinterpret_cast( buffer ); + uint32_t uDstLength = uMaxCompressedSize; + + try + { + uCompressedLength = pCompressor->compress(pSrcData, uSrcLength, pDstData, uDstLength); + } + catch(std::exception& e) + { + // At this point it didn't work even with a bigger buffer. + // Not much more we can do so just rethrow the exception. + delete buffer; + POLYVOX_THROW(std::runtime_error, "Failed to compress block data"); + } + + delete buffer; + } + + // Delete the old compressed data and assign a new one + delete m_pCompressedData; m_pCompressedData = reinterpret_cast( new uint8_t[uCompressedLength] ); + + //Copy the data across memcpy(m_pCompressedData, pDstData, uCompressedLength); m_uCompressedDataLength = uCompressedLength; - - delete pDstData; } //Flag the uncompressed data as no longer being used. diff --git a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl index c3f53407..91efdf80 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl @@ -619,7 +619,9 @@ namespace PolyVox // create the new block LoadedBlock newBlock(m_uBlockSideLength); - //Blocks start out compressed - should we change this? + // Blocks start out compressed - should we change this? + // Or maybe we should just 'seed' them with compressed data, + // rather than creating an empty block and then compressing? newBlock.block.compress(m_pCompressor); itBlock = m_pBlocks.insert(std::make_pair(v3dBlockPos, newBlock)).first; diff --git a/library/PolyVoxCore/include/PolyVoxCore/MinizCompressor.h b/library/PolyVoxCore/include/PolyVoxCore/MinizCompressor.h index 3c4ba64d..2770300e 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MinizCompressor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MinizCompressor.h @@ -11,6 +11,7 @@ namespace PolyVox MinizCompressor(); ~MinizCompressor(); + uint32_t getMaxCompressedSize(uint32_t uUncompressedInputSize); uint32_t compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength); uint32_t decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength); }; diff --git a/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.h b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.h index f17cdf51..7e75742b 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.h @@ -17,6 +17,7 @@ namespace PolyVox RLECompressor(); ~RLECompressor(); + uint32_t getMaxCompressedSize(uint32_t uUncompressedInputSize); uint32_t compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength); uint32_t decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength); }; diff --git a/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl index 6a229d9d..0b43a301 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/RLECompressor.inl @@ -14,6 +14,13 @@ namespace PolyVox { } + template + uint32_t RLECompressor::getMaxCompressedSize(uint32_t uUncompressedInputSize) + { + // In the worst case we will have a seperate Run (of length one) for each element of the input data. + return uUncompressedInputSize * sizeof(Run); + } + template uint32_t RLECompressor::compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) { diff --git a/library/polyvoxcore/source/MinizCompressor.cpp b/library/polyvoxcore/source/MinizCompressor.cpp index 30b7988c..2d749a54 100644 --- a/library/polyvoxcore/source/MinizCompressor.cpp +++ b/library/polyvoxcore/source/MinizCompressor.cpp @@ -29,6 +29,11 @@ namespace PolyVox { } + uint32_t MinizCompressor::getMaxCompressedSize(uint32_t uUncompressedInputSize) + { + return static_cast(mz_compressBound(static_cast(uUncompressedInputSize))); + } + uint32_t MinizCompressor::compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) { mz_ulong ulDstLength = uDstLength; From 09c6e2bf268f6a58ff910fd77abe65777cbb8aee Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Fri, 1 Feb 2013 16:10:10 +0100 Subject: [PATCH 33/38] More work making the compression more robust. --- .../include/PolyVoxCore/Impl/Block.inl | 47 ++++++++++--------- tests/testvolume.cpp | 4 +- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl index ed893e47..85504b64 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl @@ -141,7 +141,7 @@ namespace PolyVox template void Block::compress(Compressor* pCompressor) { - //POLYVOX_ASSERT(pCompressor, "Compressor is not valid"); + POLYVOX_ASSERT(pCompressor, "Compressor is not valid"); POLYVOX_ASSERT(m_bIsCompressed == false, "Attempted to compress block which is already flagged as compressed."); POLYVOX_ASSERT(m_tUncompressedData != 0, "No uncompressed data is present."); @@ -149,53 +149,58 @@ namespace PolyVox //modified then we don't need to redo the compression. if(m_bIsUncompressedDataModified) { + // Delete the old compressed data as we'll create a new one + delete[] m_pCompressedData; + m_pCompressedData = 0; + void* pSrcData = reinterpret_cast(m_tUncompressedData); uint32_t uSrcLength = m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType); - void* pDstData = 0; + uint8_t tempBuffer[10000]; + void* pDstData = reinterpret_cast( tempBuffer ); + uint32_t uDstLength = 10000; + uint32_t uCompressedLength = 0; try { - uint8_t buffer[1000000]; - - pDstData = reinterpret_cast( buffer ); - uint32_t uDstLength = 1000000; - uCompressedLength = pCompressor->compress(pSrcData, uSrcLength, pDstData, uDstLength); + + // Create new compressed data and copy across + m_pCompressedData = reinterpret_cast( new uint8_t[uCompressedLength] ); + memcpy(m_pCompressedData, pDstData, uCompressedLength); + m_uCompressedDataLength = uCompressedLength; } - catch(std::exception& e) + catch(std::exception&) { // It is possible for the compression to fail. A common cause for this would be if the destination // buffer is not big enough. So now we try again using a buffer that is definitely big enough. + // Note that ideally we will choose our earlier buffer size so that this almost never happens. uint32_t uMaxCompressedSize = pCompressor->getMaxCompressedSize(uSrcLength); uint8_t* buffer = new uint8_t[ uMaxCompressedSize ]; pDstData = reinterpret_cast( buffer ); - uint32_t uDstLength = uMaxCompressedSize; + uDstLength = uMaxCompressedSize; try { uCompressedLength = pCompressor->compress(pSrcData, uSrcLength, pDstData, uDstLength); + + // Create new compressed data and copy across + m_pCompressedData = reinterpret_cast( new uint8_t[uCompressedLength] ); + memcpy(m_pCompressedData, pDstData, uCompressedLength); + m_uCompressedDataLength = uCompressedLength; } - catch(std::exception& e) + catch(std::exception&) { // At this point it didn't work even with a bigger buffer. // Not much more we can do so just rethrow the exception. - delete buffer; + delete[] buffer; POLYVOX_THROW(std::runtime_error, "Failed to compress block data"); } - delete buffer; - } - - // Delete the old compressed data and assign a new one - delete m_pCompressedData; - m_pCompressedData = reinterpret_cast( new uint8_t[uCompressedLength] ); - - //Copy the data across - memcpy(m_pCompressedData, pDstData, uCompressedLength); - m_uCompressedDataLength = uCompressedLength; + delete[] buffer; + } } //Flag the uncompressed data as no longer being used. diff --git a/tests/testvolume.cpp b/tests/testvolume.cpp index bb9c784c..74a3ffa1 100644 --- a/tests/testvolume.cpp +++ b/tests/testvolume.cpp @@ -271,8 +271,8 @@ TestVolume::TestVolume() { Region region(-57, -31, 12, 64, 96, 131); // Deliberatly awkward size - m_pCompressor = new RLECompressor; - //m_pCompressor = new MinizCompressor; + //m_pCompressor = new RLECompressor; + m_pCompressor = new MinizCompressor; //Create the volumes m_pRawVolume = new RawVolume(region); From aef5373e8b7da6477fe43f321340648ec4f36c13 Mon Sep 17 00:00:00 2001 From: David Williams Date: Fri, 1 Feb 2013 16:37:13 +0100 Subject: [PATCH 34/38] Fixed case problem in folder name. --- library/{polyvoxcore => PolyVoxCore}/source/MinizCompressor.cpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename library/{polyvoxcore => PolyVoxCore}/source/MinizCompressor.cpp (100%) diff --git a/library/polyvoxcore/source/MinizCompressor.cpp b/library/PolyVoxCore/source/MinizCompressor.cpp similarity index 100% rename from library/polyvoxcore/source/MinizCompressor.cpp rename to library/PolyVoxCore/source/MinizCompressor.cpp From 8d2061bdb5296bb2dd47c096bf0f286d9ceeae95 Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Mon, 4 Feb 2013 16:38:50 +0100 Subject: [PATCH 35/38] Fixed crash with MaxVerticesPerPosition being set too low. --- .../include/PolyVoxCore/CubicSurfaceExtractor.inl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl index 5cf72fc6..72294e2c 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl @@ -26,13 +26,12 @@ namespace PolyVox // We try to avoid duplicate vertices by checking whether a vertex has already been added at a given position. // However, it is possible that vertices have the same position but different materials. In this case, the // vertices are not true duplicates and both must be added to the mesh. As far as I can tell, it is possible to have - // at most six vertices with the same position but different materials. This worst-case scenario happens when we - // have a 2x2x2 group of voxels (all with different materials) and then we delete two voxels from opposing corners. - // The vertex position at the center of this group is then going to be used by six quads all with different materials. - // One futher note - we can actually have eight quads sharing a vertex position (imagine two 1x1x10 rows of voxels - // sharing a common edge) but in this case all eight quads will not have different materials. + // at most eight vertices with the same position but different materials. For example, this worst-case scenario + // happens when we have a 2x2x2 group of voxels, all with different materials and some/all partially transparent. + // The vertex position at the center of this group is then going to be used by all eight voxels all with different + // materials. template - const uint32_t CubicSurfaceExtractor::MaxVerticesPerPosition = 6; + const uint32_t CubicSurfaceExtractor::MaxVerticesPerPosition = 8; template CubicSurfaceExtractor::CubicSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh* result, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, bool bMergeQuads, IsQuadNeeded isQuadNeeded) From 50c1c7c64b6580794dc30c4915b2d14698d1035c Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Thu, 7 Feb 2013 15:55:10 +0100 Subject: [PATCH 36/38] Removed setCompressionEnabled() from LargeVolume. --- .../include/PolyVoxCore/LargeVolume.h | 3 -- .../include/PolyVoxCore/LargeVolume.inl | 38 +++++-------------- tests/testvolume.cpp | 12 +++--- tests/testvolume.h | 4 +- 4 files changed, 18 insertions(+), 39 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h index 5cae0359..7b8316b2 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h +++ b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h @@ -279,8 +279,6 @@ namespace PolyVox /// Gets a voxel at the position given by a 3D vector VoxelType getVoxelWithWrapping(const Vector3DInt32& v3dPos, WrapMode eWrapMode = WrapModes::Border, VoxelType tBorder = VoxelType(0)) const; - //Sets whether or not blocks are compressed in memory - void setCompressionEnabled(bool bCompressionEnabled); /// Sets the number of blocks for which uncompressed data is stored void setMaxNumberOfUncompressedBlocks(uint32_t uMaxNumberOfUncompressedBlocks); /// Sets the number of blocks which can be in memory before the paging system starts unloading them @@ -368,7 +366,6 @@ namespace PolyVox //The compressor used by the Blocks to compress their data if required. Compressor* m_pCompressor; - bool m_bCompressionEnabled; bool m_bPagingEnabled; }; } diff --git a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl index 91efdf80..e92bda05 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl @@ -53,6 +53,7 @@ namespace PolyVox //////////////////////////////////////////////////////////////////////////////// /// This constructor creates a volume with a fixed size which is specified as a parameter. By default this constructor will not enable paging but you can override this if desired. If you do wish to enable paging then you are required to provide the call back function (see the other LargeVolume constructor). /// \param regValid Specifies the minimum and maximum valid voxel positions. + /// \param pCompressor An implementation of the Compressor interface which is used to compress blocks in memory. /// \param dataRequiredHandler The callback function which will be called when PolyVox tries to use data which is not currently in momory. /// \param dataOverflowHandler The callback function which will be called when PolyVox has too much data and needs to remove some from memory. /// \param bPagingEnabled Controls whether or not paging is enabled for this LargeVolume. @@ -82,7 +83,7 @@ namespace PolyVox //////////////////////////////////////////////////////////////////////////////// /// This function should never be called. Copying volumes by value would be expensive, and we want to prevent users from doing /// it by accident (such as when passing them as paramenters to functions). That said, there are times when you really do want to - /// make a copy of a volume and in this case you should look at the Volumeresampler. + /// make a copy of a volume and in this case you should look at the VolumeResampler. /// /// \sa VolumeResampler //////////////////////////////////////////////////////////////////////////////// @@ -243,30 +244,6 @@ namespace PolyVox return getVoxelWithWrapping(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), eWrapMode, tBorder); } - //////////////////////////////////////////////////////////////////////////////// - /// Enabling compression allows significantly more data to be stored in memory. - /// \param bCompressionEnabled Specifies whether compression is enabled. - //////////////////////////////////////////////////////////////////////////////// - template - void LargeVolume::setCompressionEnabled(bool bCompressionEnabled) - { - //Early out - nothing to do - if(m_bCompressionEnabled == bCompressionEnabled) - { - return; - } - - m_bCompressionEnabled = bCompressionEnabled; - - if(m_bCompressionEnabled) - { - //If compression has been enabled then we need to start honouring the max number of - //uncompressed blocks. Because compression has been disabled for a while we might have - //gone above that limit. Easiest solution is just to clear the cache and start again. - clearBlockCache(); - } - } - //////////////////////////////////////////////////////////////////////////////// /// Increasing the size of the block cache will increase memory but may improve performance. /// You may want to set this to a large value (e.g. 1024) when you are first loading your @@ -472,6 +449,7 @@ namespace PolyVox //Debug mode validation POLYVOX_ASSERT(uBlockSideLength > 0, "Block side length cannot be zero."); POLYVOX_ASSERT(isPowerOf2(uBlockSideLength), "Block side length must be a power of two."); + POLYVOX_ASSERT(m_pCompressor, "You must provide a compressor for the LargeVolume to use."); //Release mode validation if(uBlockSideLength == 0) @@ -482,6 +460,10 @@ namespace PolyVox { POLYVOX_THROW(std::invalid_argument, "Block side length must be a power of two."); } + if(!m_pCompressor) + { + POLYVOX_THROW(std::invalid_argument, "You must provide a compressor for the LargeVolume to use."); + } m_uTimestamper = 0; m_uMaxNumberOfUncompressedBlocks = 16; @@ -489,7 +471,6 @@ namespace PolyVox m_uMaxNumberOfBlocksInMemory = 1024; m_v3dLastAccessedBlockPos = Vector3DInt32(0,0,0); //There are no invalid positions, but initially the m_pLastAccessedBlock pointer will be null; m_pLastAccessedBlock = 0; - m_bCompressionEnabled = true; this->m_regValidRegion = regValidRegion; @@ -532,7 +513,8 @@ namespace PolyVox m_funcDataOverflowHandler(ConstVolumeProxy, reg); } - if(m_bCompressionEnabled) { + if(m_pCompressor) + { for(uint32_t ct = 0; ct < m_vecUncompressedBlockCache.size(); ct++) { // find the block in the uncompressed cache @@ -657,7 +639,7 @@ namespace PolyVox } //If we are allowed to compress then check whether we need to - if((m_bCompressionEnabled) && (m_vecUncompressedBlockCache.size() == m_uMaxNumberOfUncompressedBlocks)) + if((m_pCompressor) && (m_vecUncompressedBlockCache.size() == m_uMaxNumberOfUncompressedBlocks)) { int32_t leastRecentlyUsedBlockIndex = -1; uint32_t uLeastRecentTimestamp = (std::numeric_limits::max)(); diff --git a/tests/testvolume.cpp b/tests/testvolume.cpp index 74a3ffa1..03a35b53 100644 --- a/tests/testvolume.cpp +++ b/tests/testvolume.cpp @@ -271,8 +271,8 @@ TestVolume::TestVolume() { Region region(-57, -31, 12, 64, 96, 131); // Deliberatly awkward size - //m_pCompressor = new RLECompressor; - m_pCompressor = new MinizCompressor; + m_pCompressor = new RLECompressor; + //m_pCompressor = new MinizCompressor; //Create the volumes m_pRawVolume = new RawVolume(region); @@ -312,7 +312,7 @@ TestVolume::~TestVolume() * RawVolume Tests */ -/*void TestVolume::testRawVolumeDirectAccessAllInternalForwards() +void TestVolume::testRawVolumeDirectAccessAllInternalForwards() { int32_t result = 0; @@ -398,13 +398,13 @@ void TestVolume::testRawVolumeSamplersWithExternalBackwards() result = testSamplersWithWrappingBackwards(m_pRawVolume, -1, -3, -2, 2, 5, 4); } QCOMPARE(result, static_cast(-769775893)); -}*/ +} /* * SimpleVolume Tests */ -/*void TestVolume::testSimpleVolumeDirectAccessAllInternalForwards() +void TestVolume::testSimpleVolumeDirectAccessAllInternalForwards() { int32_t result = 0; QBENCHMARK @@ -482,7 +482,7 @@ void TestVolume::testSimpleVolumeSamplersWithExternalBackwards() result = testSamplersWithWrappingBackwards(m_pSimpleVolume, -1, -3, -2, 2, 5, 4); } QCOMPARE(result, static_cast(-769775893)); -}*/ +} /* * LargeVolume Tests diff --git a/tests/testvolume.h b/tests/testvolume.h index 59a63ec8..0c363109 100644 --- a/tests/testvolume.h +++ b/tests/testvolume.h @@ -37,7 +37,7 @@ public: ~TestVolume(); private slots: - /*void testRawVolumeDirectAccessAllInternalForwards(); + void testRawVolumeDirectAccessAllInternalForwards(); void testRawVolumeSamplersAllInternalForwards(); void testRawVolumeDirectAccessWithExternalForwards(); void testRawVolumeSamplersWithExternalForwards(); @@ -53,7 +53,7 @@ private slots: void testSimpleVolumeDirectAccessAllInternalBackwards(); void testSimpleVolumeSamplersAllInternalBackwards(); void testSimpleVolumeDirectAccessWithExternalBackwards(); - void testSimpleVolumeSamplersWithExternalBackwards();*/ + void testSimpleVolumeSamplersWithExternalBackwards(); void testLargeVolumeDirectAccessAllInternalForwards(); void testLargeVolumeSamplersAllInternalForwards(); From 687dbe40cb89c25a410e62f2765d11bb3208689d Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Thu, 7 Feb 2013 16:06:46 +0100 Subject: [PATCH 37/38] Updated Changelog and credits. --- CHANGELOG.txt | 8 ++++++++ CREDITS.txt | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index b96f18ad..1b772672 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -2,6 +2,14 @@ Changes for PolyVox version 0.3 =============================== This release has focused on... (some introduction here). +LargeVolume +----------- +It is now possible to provide custom compressors for the data which is stored in a LargeVolume. Two compressor implementation are provided with PolyVox - RLECompressor which is suitable for cubic-style terrain, and MinizCompressor which uses the 'miniz' zlib implementation for smooth terrain or general purpose use. Users can provide their own implementation of the compressor interface if they wish. + +Note that the setCompressionEnabled() functionality has been removed and a compressor must always be provided when constructing the volume. The ability to disable compression was of questionable benefit and was complicating the logic in the code. In practice it is still possible to mostly disable compression by setting the maximum number of uncompressed blocks to a high value by calling setMaxNumberOfUncompressedBlocks(). + +These changes regarding compression and paging have also affected the LargeVolume constructors(s). Please see the API docs to see how they look now. + Error Handling -------------- PolyVox now has it's own POLYVOX_ASSERT() macro rather than using the standard assert(). This has some advantages such as allowing a message to be printed and providing file/line information, and it is also possible to enable/disable it independantly of whether you are making a debug or release build diff --git a/CREDITS.txt b/CREDITS.txt index a04632c4..35f5a606 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1,3 +1,7 @@ David Williams - Lead programmer. Matthew Williams - Linux port, build system, support and maintenance. -Oliver Schneider - Very large Volumes \ No newline at end of file +Oliver Schneider - Very large Volumes + +Acknowledgements +---------------- +PolyVox uses the 'miniz' for data compression. 'miniz' is public domain software and does not affect the license of PolyVox or of code using PolyVox. More details here: http://code.google.com/p/miniz/ \ No newline at end of file From c3801db4e29b1105f6c03424ee0e5ae4364d40f2 Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Thu, 7 Feb 2013 16:20:05 +0100 Subject: [PATCH 38/38] Set tests to use MinizCompressor. --- tests/testvolume.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/testvolume.cpp b/tests/testvolume.cpp index 03a35b53..2bd15cbd 100644 --- a/tests/testvolume.cpp +++ b/tests/testvolume.cpp @@ -271,8 +271,8 @@ TestVolume::TestVolume() { Region region(-57, -31, 12, 64, 96, 131); // Deliberatly awkward size - m_pCompressor = new RLECompressor; - //m_pCompressor = new MinizCompressor; + //m_pCompressor = new RLECompressor; + m_pCompressor = new MinizCompressor; //Create the volumes m_pRawVolume = new RawVolume(region);